Handling Events for OLEObject CommandButtons created at Runtime - events

I have struggled with this problem for while now...I want to do something very simple. I want to create multiple commandbuttons at runtime, and then handle events for these commandbuttons with one procedure. So I have built a "withevents" class to handle the automation, but my code is not working. When I run Test(), the CommandButton is created, but when I click on it...there is no messagebox response...I cannot find the error..Please any help would be great!!
Class cTest
Public WithEvents Button As MSForms.CommandButton
Public Sub Button_Click()
s = MsgBox("Hello", vbOKOnly)
End Sub
Module 1
Public TestCollection As Collection
Sub Test()
Set TestCollection = New Collection
Dim Btn As CommandButton
Dim OLEBtnObj As cTest
Set OLEBtnObj = New cTest
Set Btn = Sheet1.OLEObjects.Add(ClassType:="Forms.CommandButton.1", link:=False,_ DisplayAsIcon:=False, Left:=368.25, Top:=51, Width:=44.25, Height:=24).Object
Set OLEBtnObj.Button = Btn
TestCollection.Add Item:=OLEBtnObj
End Sub

I have one rather impractical solution. To test it place the following code in Sheet Class Module (see attached image). The Me.CodeName refers to Code-Name of the sheet.
For each new Sheet1-button a new event handled will be added. This event handler will execute the common event handler and pass the name of the clicked command button to it.
' Standard Module
Sub test()
' adds three buttons to Sheet1 with click-event handlers
Sheet1.AddButton
ActiveCell.Offset(5, 0).Activate
Sheet1.AddButton
ActiveCell.Offset(5, 0).Activate
Sheet1.AddButton
End Sub
' Sheet1 Class Module
Option Explicit
' Add Microsoft Visual Basic For Applications Extensibility
Public Function AddButton() As MSForms.CommandButton
Dim msFormsCommandButton As MSForms.CommandButton
Set msFormsCommandButton = Me.OLEObjects.Add(ClassType:="Forms.CommandButton.1").Object
CreateEventHandler msFormsCommandButton.Name
Set AddButton = msFormsCommandButton
End Function
Private Sub CommonButton_Click(ByVal buttonName As String)
MsgBox "You clicked button [" & buttonName & "]"
End Sub
Private Sub CreateEventHandler(ByVal buttonName As String)
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim codeText As String
Dim LineNum As Long
Set VBComp = ThisWorkbook.VBProject.VBComponents(Me.CodeName)
Set CodeMod = VBComp.CodeModule
LineNum = CodeMod.CountOfLines + 1
codeText = codeText & "Private Sub " & buttonName & "_Click()" & vbCrLf
codeText = codeText & " Dim buttonName As String" & vbCrLf
codeText = codeText & " buttonName = """ & buttonName & "" & vbCrLf
codeText = codeText & " CommonButton_Click buttonName" & vbCrLf
codeText = codeText & "End Sub"
CodeMod.InsertLines LineNum, codeText
End Sub

Related

How to retrieve data from external device through Winsock to Microsoft Visual Basic (VB6)

I need to make an application that needs to retrieve data from an external terminal booking device to my application, with a telnet connection and show it on the txtOutput textbox.
I am pretty new in VB and used some time to learn the basics of the language.
First I created a Standard EXE project and added the Winsock control to the form.
I made a ping-request to the IP address I wanted to have connection too and it works.
Then I want to send a command to the external device. I want the booking-terminal to give me feedback to the txtOutput for me to read.
I made the connection and as much as I can see, I do send my messages to the terminal. But I don't get any responses from it! Nothing from it is viewed on my txtOutput.
How can that be?
Here is my code:
Dim IPAddress As String
Dim PortNum As Integer
Private Sub cmdConnect_Click()
Winsock.Close
Winsock.RemoteHost = txtIpaddress.Text
IPAddress = Winsock.RemoteHost
PortNum = CStr(txtPortnr.Text)
If (Val(PortNum) > 65535) Then
Winsock.RemotePort = (Val(PortNum) - 65535)
PortNum = Winsock.RemotePort
Else
Winsock.RemotePort = Val(PortNum)
PortNum = Winsock.RemotePort
End If
Winsock.Connect
Module1.send_to_buffer ("Attempting connection to: " & IPAddress & ":" & CStr(PortNum))
Call wsock_status
End Sub
Private Sub Winsock_Connect()
Module1.send_to_buffer ("Succeeded connection to: " & IPAddress & ":" & CStr(PortNum))
txtSend.SetFocus
End Sub
Private Sub cmdSend_Click()
Dim strSData As String
Dim message_to_send As String
If (Winsock.State = 0) Then
Module1.send_to_buffer ("You need to connect first!")
txtSend.Text = ""
Else
strSData = txtSend.Text
Winsock.SendData strSData & vbCrLf
message_to_send = txtSend.Text
If (message_to_send <> "") Then
Winsock.SendData message_to_send & vbCrLf
Module1.send_to_buffer_norm (txtSend.Text)
txtSend.Text = ""
txtSend.SetFocus
Else
Module1.send_to_buffer ("Nothing to send!")
txtSend.Text = ""
txtSend.SetFocus
End If
End If
End Sub
Private Sub terminalConnector_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
Winsock.GetData strData
If (Len(txtOutput.Text) = 0) Then
txtOutput.Text = strData & vbCrLf
Else
txtOutput.Text = txtOutput.Text & strData & vbCrLf
End If
End Sub
Private Sub cmdDisconnect_Click()
Dim Counter As Long
If (Winsock.State <> 0) Then
Winsock.Close
Call wsock_status
Module1.send_to_buffer ("Connection to " & IPAddress & ":" & CStr(PortNum) & " closed.")
End If
End Sub
Private Sub Winsock_Close()
Module1.send_to_buffer ("Disconnected from: " & IPAddress & ":" & CStr(PortNum))
Winsock.Close
End Sub
and Module1 code:
Public Function send_to_buffer(text_to_display As String)
If (Len(terminalConnector.txtOutput.Text) = 0) Then
terminalConnector.txtOutput.Text = "*** " & text_to_display
Else
terminalConnector.txtOutput.Text = terminalConnector.txtOutput.Text & vbCrLf & "*** " & text_to_display & vbCrLf & vbCrLf
End If
End Function
Public Function send_to_buffer_norm(text_to_input As String)
If (Len(terminalConnector.txtOutput.Text) = 0) Then
terminalConnector.txtOutput.Text = "> " & text_to_input & vbCrLf
Else
terminalConnector.txtOutput.Text = terminalConnector.txtOutput.Text & "> " & text_to_input & vbCrLf
End If
End Function
Thanks in advance
The DataArrival event is named wrongly :
in your code it is :
Private Sub terminalConnector_DataArrival(ByVal bytesTotal As Long)
but it should be the name of your winsock control :
Private Sub Winsock_DataArrival(ByVal bytesTotal As Long)
You can always select the controlname in the combobox on the left above your code window in the IDE and then select the eventname in the combobox on the right above your code window in the IDE, which will put the outlines of the event code in your code window.
Or you can double-click the control on the design window in the IDE, which will bring you to the code window and put the outlines of an event code in your code window .. you can then select the event you want in the combobox on the right above your code window
A side comment : Winsock might is not the best name for a winsock control, it is best to give it a more unique same, which could be as simple as wskConnection or wskTerminal
When you download MZ-tools you can "Review Source Code" which will show you any procedures and variables that will not be called or used in your program ... this will often give an extra hint to misnamed variables or procedures
You Send routine is wrong :
Private Sub cmdSend_Click()
Dim strSData As String
txtSend.Text = strSData
Winsock.SendData strSData
End Sub
You are showing strSata in txtSend ... while strSData is still an empty string .. after that you send the empty string via the Winsock control
you probably meant :
Private Sub cmdSend_Click()
Dim strSData As String
strSData = txtSend.Text
Winsock.SendData strSData
End Sub
Which reads txtSend.Text into your string variable, and then sends that via the Winsock control
The server probably wants some special character at the end of your string, so dont forget to add that ... usually you have to add a cariage return :
strSData = strSData & vbCr

How do I generate labels on the fly when double clicking text boxes in visual basic?

This code is an on the fly label creator for each textbox on a form. Make sure every text box needed on your form is linked to the event handler using double/single click.. I've found this code works but if anyone has a tidier version of this code feel free to post a revision.
Public Class Form1
Private Sub MyEventRoutine( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles TextBox2.DoubleClick, TextBox1.DoubleClick
'get textbox
Dim myTextBox As TextBox = sender
'decl label associated with textbox
Dim thelabeltochange As Control
'decl label-name finder code
Dim lblname As String
lblname = myTextBox.Name & "label"
If Me.Controls.Find(lblname, True).Length = 0 Then
Dim lab As New Label
lab.Name = myTextBox.Name & "label"
lab.Size = New Size(40, 20)
lab.Text = "Correct"
lab.Location = New Point(myTextBox.Left + 89, myTextBox.Top)
Me.Controls.Add(lab) 'this should newly created label to your form
lab.BringToFront()
Debug.Write("LABEL name >" & lblname & "< generated on first double click" & vbCrLf)
Else
For Each tmp As Control In Me.Controls
If tmp.Name = myTextBox.Name & "label" Then
Debug.Write("label exists and has name" & vbCrLf)
thelabeltochange = tmp
If thelabeltochange.Text = "Correct" Then
Debug.Write("label set as correct and is now wrong" & vbCrLf)
thelabeltochange.Text = "Wrong"
thelabeltochange.BringToFront()
ElseIf thelabeltochange.Text = "Wrong" Then
Debug.Write("label set as wrong and is now To do" & vbCrLf)
thelabeltochange.Text = "To do"
thelabeltochange.BringToFront()
ElseIf thelabeltochange.Text = "To do" Then
Debug.Write("label set as To do and is now deleted" & vbCrLf)
Me.Controls.Remove(thelabeltochange)
End If
End If
Next
End If
End Sub
End Class

How can I do zonal OCR in VB6?

As you can see down there i made a programme that scans a document and optionally get the page info and material & size infos and date info.
When i use OCR scanning like this:
Dim Mdoc As MODI.Document
Dim Mlay As MODI.Layout
Dim fso As Scripting.FileSystemObject
Dim logfile As Object
Public Function ScanMan(ByVal Name As String, ByVal Path As String) As String
Set Mdoc = New MODI.Document
'Set Mdoc = CreateObject("MODI.Document")
Set fso = New Scripting.FileSystemObject
DoEvents
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''' Create OCRLog File '''''''''''''''''''
OCRPath = App.Path & "\OCR Results Log\"
OCRName = Str(DateTime.Date) & " OCRresults"
If fso.FolderExists(OCRPath) = False Then
fso.CreateFolder (OCRPath)
End If
If fso.FileExists(OCRPath & OCRName & ".txt") = False Then
fso.CreateTextFile OCRPath & OCRName & ".txt"
End If
Set logfile = fso.OpenTextFile(OCRPath & OCRName & ".txt", ForAppending)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error GoTo OCRErr
DoEvents
Mdoc.Create Path & "\" & Name
Mdoc.Images(0).OCR miLANG_ENGLISH, True, True
logfile.Write Mdoc.Images(0).Layout.Text
ScanMan = Mlay.Text
Mdoc.Close False
Set Mlay = Nothing
Set Mdoc = Nothing
Exit Function
OCRErr:
logfile.WriteLine "OCR given (" & Err.Number & ") numbered (" & Err.Description & ") error."
logfile.Close
End Function
This gets the whole page but i just want those 3 spesific area to be scanned so how can i achive that? Is there any function for that? Which scans only X,Y coordinates?
A vb6 snippet
Sub TestTextSelection()
Dim miTextSel As MODI.IMiSelectableItem
Dim miSelectRects As MODI.miSelectRects
Dim miSelectRect As MODI.miSelectRect
Dim strTextSelInfo As String
Set miTextSel = MiDocView1.TextSelection
Set miSelectRects = miTextSel.GetSelectRects
strTextSelInfo = _
"Bounding rectangle page & coordinates: " & vbCrLf
For Each miSelectRect In miSelectRects
With miSelectRect
strTextSelInfo = strTextSelInfo & _
.PageNumber & ", " & .Top & ", " & _
.Left & ", " & .Bottom & ", " & _
.Right & vbCrLf
End With
Next
MsgBox strTextSelInfo, vbInformation + vbOKOnly, _
"Text Selection Info"
Set miSelectRect = Nothing
Set miSelectRects = Nothing
Set miTextSel = Nothing
End Sub
Though the question is tagged as vb6 but answer is from vb.Net 2010. I hope vb.NET could easily be converted to vb6, only matters is just a few more time.
The basic idea is to create an xml file from image and then run a query over the xml file to fetch text of the required block surrounded by (x1,y1) and (x2,y2).
The core class
Imports System
Imports System.IO
Imports System.Xml
Imports System.Linq
Imports MODI
Public Class clsCore
Public Sub New()
'blah blah blah
End Sub
Public Function GetTextFromCoordinates(ByVal iPath$, ByVal x1&, ByVal y1&, ByVal x2&, ByVal y2&) As String
Try
Dim xDoc As XElement = Me.ConvertImage2XML(iPath)
If IsNothing(xDoc) = False Then
Dim result As New XElement(<text/>)
Dim query = xDoc...<wd>.Where(Function(c) Val(CStr(c.#left)) >= x1 And Val(CStr(c.#right)) <= x2 And Val(CStr(c.#top)) >= y1 And Val(CStr(c.#bottom)) <= y2)
For Each ele As XElement In query
result.Add(CStr(ele.Value) & " ")
Next ele
Return Trim(result.Value)
Else
Return ""
End If
Catch ex As Exception
Console.WriteLine(ex.ToString)
Return ex.ToString
End Try
End Function
Private Function ConvertImage2XML(ByVal iPath$) As XElement
Try
If File.Exists(iPath) = True Then
Dim miDoc As New MODI.Document
Dim result As New XElement(<image path=<%= iPath %>/>)
miDoc.Create(iPath)
For Each miImg As MODI.Image In miDoc.Images
Dim page As New XElement(<page id=<%= result...<page>.Count + 1 %>/>)
miImg.OCR()
For Each miWord As MODI.Word In miImg.Layout.Words
Dim wd As New XElement(<wd block=<%= miWord.RegionId.ToString %>><%= miWord.Text %></wd>)
For Each miRect As MODI.MiRect In miWord.Rects
wd.Add(New XAttribute("left", miRect.Left))
wd.Add(New XAttribute("top", miRect.Top))
wd.Add(New XAttribute("right", miRect.Right))
wd.Add(New XAttribute("bottom", miRect.Bottom))
Next miRect
page.Add(wd)
Next miWord
result.Add(page)
Next miImg
Return result
Else
Return Nothing
End If
Catch ex As Exception
Console.WriteLine(ex.ToString)
Return Nothing
End Try
End Function
End Class
the main module
Imports System
Imports System.IO
Imports System.Text.RegularExpressions
Module modMain
Sub Main()
Dim iPath$ = "", iPos$ = "150,825,1400,1200"
Console.WriteLine("Enter path to file:")
iPath = Console.ReadLine()
Console.WriteLine("")
Console.WriteLine("Enter co-ordinates(i.e., x1,y1,x2,y2 or 150,825,1400,1200):")
iPos = Console.ReadLine()
Dim tmp As String() = Regex.Split(iPos, "\D+")
Dim outText$ = New clsCore().GetTextFromCoordinates(iPath, tmp(0), tmp(1), tmp(2), tmp(3))
Console.WriteLine("")
Console.WriteLine(String.Format("{0}[({1},{2})-({3},{4})]:{5}{5}{6}", Dir(iPath), tmp(0), tmp(1), tmp(2), tmp(3), vbCrLf, outText))
Console.ReadLine()
End Sub
End Module
UPDATE
The following example reports the page number and the coordinates of the bounding rectangle around the user's image selection in the viewer control. And which can be used later within picturebox.
Sub TestImageSelection()
Dim miImageSel As MODI.IMiSelectableImage
Dim lngPageNo As Long
Dim lngLeft As Long, lngTop As Long
Dim lngRight As Long, lngBottom As Long
Dim strImageSelInfo As String
Set miImageSel = MiDocView1.ImageSelection
miImageSel.GetBoundingRect lngPageNo, _
lngLeft, lngTop, lngRight, lngBottom
strImageSelInfo = _
"Page number: " & lngPageNo & vbCrLf & _
"Bounding rectangle coordinates: " & vbCrLf & _
lngLeft & ", " & lngTop & ", " & _
lngRight & ", " & lngBottom
MsgBox strImageSelInfo, vbInformation + vbOKOnly, _
"Image Selection Info"
Set miImageSel = Nothing
End Sub
Hope this helps.
I used image and pic boxes to crop and resize a picture exactly to HD pixels and size for inclusion in a HD movie. I moved the picture about with slider controls (eg PicSize.Value)
The picture box is set to 1900x1080 pixels off screen with Visible=false.
The image box size has Stretch set to true with size is not critical and shows a smaller version of the final cropped pic.
I save the picture box as a bmp so it nicely integrates with my AVCHD video in the Adobe editor being the same frame size as the video.
This was the main subroutine:
-Private Sub Convert()
'Creates a cropped and/or magnified fixed pixel 1900x1080 picture
Dim file_name As String, LeftPos As Long
Picture2.Picture = LoadPicture("")
DoEvents
' Resize the picture.
LeftPos = 950 + HPos.Value - PicSize.Value / 2 + PicWidth.Value * 20
Picture2.PaintPicture Picture1.Picture, _
LeftPos, VPos.Value, _
PicSize.Value - (PicSize.Value * (PicWidth.Value / 50)), _
PicSize.Value * (Aspect.Value / 100)
Picture2.Picture = Picture2.Image
TopValue.Caption = VPos.Value
HPosValue.Caption = HPos.Value
SizeValue.Caption = PicSize.Value
AspectValue.Caption = Aspect.Value - 75
StretchValue.Caption = PicWidth.Value
Image1.Picture = Picture2.Image 'preview it
End Sub

VB6.0 with DataControl Database Programming

can you help out access the database... I have been reading some tutorials but I don't know where to start doing this one. I used DataControl to access the database. First, the program will prompt for the ID Number and then Search for the further information and display it in texboxes when Search Employee button clicked. I know how to set the properties of textboxes in order to appear the value of my database to my output without clicking the Search Employee button but I want to click first the button Search Employee. I'm a beginner in VB6. Please help me out! I need this project now.
Ok, I had some time to spare, here you go, first add a reference to Microsoft ActiveX Data Objects 2.X Library:
Form1 Code:
Option Explicit
''Add the following items to your form and name them as indicated:
''Four(4) text boxes - Named: tbIDNumber, tbName, tbAddress, and tbContactName.
''One(1) Command button - Named Command1
Private Sub Command1_Click()
Dim rs As ADODB.Recordset
Dim DB As cDatabase
Dim l As Long
Set rs = New ADODB.Recordset
Set DB = New cDatabase
With DB
.DBCursorType = adOpenForwardOnly
.DBLockType = adLockReadOnly
.DBOptions = adCmdText
.DSNName = "Your_DSN_Name"
.SQLUserID = "Your_SQL_Login_Name"
.SQLPassword = "Your_SQL_Login_Password"
Set rs = .GetRS("Select Name, Address, ContactNumber FROM YourTableName WHERE IDNumber = '" & tbIDNumber.Text & "'")
End With
If rs.RecordCount > 0 Then
tbName.Text = rs(0).Value & ""
tbAddress.Text = rs(1).Value & ""
tbContactName.Text = rs(2).Value & ""
End If
Exit_Sub:
rs.Close
Set rs = Nothing
Set DB = Nothing
End Sub
Add a Class Module Object to your project and name it cDatabase. Then copy the following Code into it:
Option Explicit
Private m_eDBCursorType As ADODB.CursorTypeEnum 'Cursor (Dynamic, Forward Only, Keyset, Static)
Private m_eDBLockType As ADODB.LockTypeEnum 'Locks (BatchOptimistic,Optimistic,Pessimistic, Read Only)
Private m_eDBOptions As ADODB.CommandTypeEnum 'DB Options
Private m_sDSNName As String
Private m_sSQLUserID As String
Private m_sSQLPassword As String
Private cn As ADODB.Connection
Private Sub Class_Initialize()
m_eDBCursorType = adOpenForwardOnly
m_eDBLockType = adLockReadOnly
m_eDBOptions = adCmdText
End Sub
Private Function ConnectionString() As String
ConnectionString = "DSN=" & m_sDSNName & "" & _
";UID=" & m_sSQLUserID & _
";PWD=" & m_sSQLPassword & ";"
''If you are using MS Access as your back end you will need to change the connection string to the following:
''ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
''If you are using a DNS-Less connection to SQL Server, then you will need to change the connection string to the following:
''ConnectionString = "Data Source=myServerAddress;Initial Catalog=myDataBase;User Id=" & m_sSQLUserID & ";Password=" & m_sSQLPassword & ";"
''You can find more Connection Strings at http://connectionstrings.com/
End Function
Private Sub GetCN()
On Error GoTo GetCN_Error
If cn.State = 0 Then
StartCN:
Set cn = New ADODB.Connection
cn.Open ConnectionString
With cn
.CommandTimeout = 0
.CursorLocation = adUseClient
End With
End If
On Error GoTo 0
Exit Sub
GetCN_Error:
If Err.Number = 91 Then
Resume StartCN
Else
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure GetCN of Module modDatabaseConnections"
End If
End Sub
Public Function GetRS(sSQL As String) As ADODB.Recordset
Dim eRS As ADODB.Recordset
On Error GoTo GetRS_Error
TryAgain:
If Len(Trim(sSQL)) > 0 Then
Call GetCN
Set eRS = New ADODB.Recordset 'Creates record set
eRS.Open sSQL, cn, m_eDBCursorType, m_eDBLockType, m_eDBOptions
Set GetRS = eRS
Else
MsgBox "You have to submit a SQL String"
End If
On Error GoTo 0
Exit Function
GetRS_Error:
If Err.Number = 91 Then
Call GetCN
GoTo TryAgain
ElseIf Err.Number = -2147217900 Then
Exit Function
Else
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure GetRS of Module" & vbCrLf & vbCrLf & "SQL - " & sSQL
End If
End Function
Public Property Get DBOptions() As ADODB.CommandTypeEnum
DBOptions = m_eDBOptions
End Property
Public Property Let DBOptions(ByVal eDBOptions As ADODB.CommandTypeEnum)
m_eDBOptions = eDBOptions
End Property
Public Property Get DBCursorType() As ADODB.CursorTypeEnum
DBCursorType = m_eDBCursorType
End Property
Public Property Let DBCursorType(ByVal eDBCursorType As ADODB.CursorTypeEnum)
m_eDBCursorType = eDBCursorType
End Property
Public Property Get DBLockType() As ADODB.LockTypeEnum
DBLockType = m_eDBLockType
End Property
Public Property Let DBLockType(ByVal eDBLockType As ADODB.LockTypeEnum)
m_eDBLockType = eDBLockType
End Property
Public Property Get DSNName() As String
DSNName = m_sDSNName
End Property
Public Property Let DSNName(ByVal sDSNName As String)
m_sDSNName = sDSNName
End Property
Public Property Get SQLUserID() As String
SQLUserID = m_sSQLUserID
End Property
Public Property Let SQLUserID(ByVal sSQLUserID As String)
m_sSQLUserID = sSQLUserID
End Property
Public Property Get SQLPassword() As String
SQLPassword = m_sSQLPassword
End Property
Public Property Let SQLPassword(ByVal sSQLPassword As String)
m_sSQLPassword = sSQLPassword
End Property
This should do the trick.

How to add events to Controls created at runtime in Excel with VBA

I would like to add a Control and an associated event at runtime in Excel using VBA but I don't know how to add the events.
I tried the code below and the Button is correctly created in my userform but the associated click event that should display the hello message is not working.
Any advice/correction would be welcome.
Dim Butn As CommandButton
Set Butn = UserForm1.Controls.Add("Forms.CommandButton.1")
With Butn
.Name = "CommandButton1"
.Caption = "Click me to get the Hello Message"
.Width = 100
.Top = 10
End With
With ThisWorkbook.VBProject.VBComponents("UserForm1.CommandButton1").CodeModule
Line = .CountOfLines
.InsertLines Line + 1, "Sub CommandButton1_Click()"
.InsertLines Line + 2, "MsgBox ""Hello!"""
.InsertLines Line + 3, "End Sub"
End With
UserForm1.Show
The code for adding a button at runtime and then to add events is truly as simple as it is difficult to find out. I can say that because I have spent more time on this perplexity and got irritated more than in anything else I ever programmed.
Create a Userform and put in the following code:
Option Explicit
Dim ButArray() As New Class2
Private Sub UserForm_Initialize()
Dim ctlbut As MSForms.CommandButton
Dim butTop As Long, i As Long
'~~> Decide on the .Top for the 1st TextBox
butTop = 30
For i = 1 To 10
Set ctlbut = Me.Controls.Add("Forms.CommandButton.1", "butTest" & i)
'~~> Define the TextBox .Top and the .Left property here
ctlbut.Top = butTop: ctlbut.Left = 50
ctlbut.Caption = Cells(i, 7).Value
'~~> Increment the .Top for the next TextBox
butTop = butTop + 20
ReDim Preserve ButArray(1 To i)
Set ButArray(i).butEvents = ctlbut
Next
End Sub
Now you need to add a Class Module to your code for the project. Please remember it's class module, not Standard Module.
The Object butEvents is the button that was clicked.
Put in the following simple code (in my case the class name is Class2).
Public WithEvents butEvents As MSForms.CommandButton
Private Sub butEvents_click()
MsgBox "Hi Shrey from " & butEvents.Caption
End Sub
That's it. Now run it!
Try this:
Sub AddButtonAndShow()
Dim Butn As CommandButton
Dim Line As Long
Dim objForm As Object
Set objForm = ThisWorkbook.VBProject.VBComponents("UserForm1")
Set Butn = objForm.Designer.Controls.Add("Forms.CommandButton.1")
With Butn
.Name = "CommandButton1"
.Caption = "Click me to get the Hello Message"
.Width = 100
.Top = 10
End With
With objForm.CodeModule
Line = .CountOfLines
.InsertLines Line + 1, "Sub CommandButton1_Click()"
.InsertLines Line + 2, "MsgBox ""Hello!"""
.InsertLines Line + 3, "End Sub"
End With
VBA.UserForms.Add(objForm.Name).Show
End Sub
This permanently modifies UserForm1 (assuming you save your workbook). If you wanted a temporary userform, then add a new userform instead of setting it to UserForm1. You can then delete the form once you're done with it.
Chip Pearson has some great info about coding the VBE.
DaveShaw, thx for this code man!
I have used it for a togglebutton array (put a 'thumbnail-size' picture called trainer.jpg in the same folder as the excel file for a togglebutton with a picture in it). In the 'click' event the invoker is also available (by the object name as a string)
In the form:
Dim CreateTrainerToggleButtonArray() As New ToggleButtonClass
Private Sub CreateTrainerToggleButton(top As Integer, id As Integer)
Dim pathToPicture As String
pathToPicture = ThisWorkbook.Path & "\trainer.jpg"
Dim idString As String
idString = "TrainerToggleButton" & id
Dim cCont As MSForms.ToggleButton
Set cCont = Me.Controls.Add _
("Forms.ToggleButton.1")
With cCont
.Name = idString
.Width = 20
.Height = 20
.Left = 6
.top = top
.picture = LoadPicture(pathToPicture)
End With
ReDim Preserve CreateTrainerToggleButtonArray(1 To id)
Set CreateTrainerToggleButtonArray(id).ToggleButtonEvents = cCont
CreateTrainerToggleButtonArray(id).ObjectName = idString
End Sub
and a class "ToggleButtonClass"
Public WithEvents ToggleButtonEvents As MSForms.ToggleButton
Public ObjectName As String
Private Sub ToggleButtonEvents_click()
MsgBox "DaveShaw is the man... <3 from your friend: " & ObjectName
End Sub
Now just simple call from UserForm_Initialize
Private Sub UserForm_Initialize()
Dim index As Integer
For index = 1 To 10
Call CreateTrainerToggleButton(100 + (25 * index), index)
Next index
End Sub
This was my solution to add a commandbutton and code without using classes
It adds a reference to allow access to vbide
Adds the button
Then writes a function to handle the click event in the worksheet
Sub AddButton()
Call addref
Set rng = DestSh.Range("B" & x + 3)
'Set btn = DestSh.Buttons.Add(rng.Left, rng.Top, rng.Width, rng.Height)
Set myButton = ActiveSheet.OLEObjects.Add(ClassType:="Forms.CommandButton.1", Left:=rng.Left, Top:=rng.Top, Height:=rng.Height * 3, Width:=rng.Width * 3)
DoEvents
With myButton
'.Placement = XlPlacement.xlFreeFloating
.Object.Caption = "Export"
.Name = "BtnExport"
.Object.PicturePosition = 1
.Object.Font.Size = 14
End With
Stop
myButton.Object.Picture = LoadPicture("F:\Finalised reports\Templates\Macros\evolution48.bmp")
Call CreateButtonEvent
End Sub
Sub addref()
On Error Resume Next
Application.VBE.ActiveVBProject.References.AddFromFile "C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
Application.VBE.ActiveVBProject.References.AddFromFile "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
End Sub
Private Sub CreateButtonEvent()
On Error GoTo errtrap
Dim oXl As Application: Set oXl = Application
oXl.EnableEvents = False
oXl.DisplayAlerts = False
oXl.ScreenUpdating = False
oXl.VBE.MainWindow.Visible = False
Dim oWs As Worksheet
Dim oVBproj As VBIDE.VBProject
Dim oVBcomp As VBIDE.VBComponent
Dim oVBmod As VBIDE.CodeModule '
Dim lLine As Single
Const QUOTE As String = """"
Set oWs = Sheets("Contingency")
Set oVBproj = ThisWorkbook.VBProject
Set oVBcomp = oVBproj.VBComponents(oWs.CodeName)
Set oVBmod = oVBcomp.CodeModule
With oVBmod
lLine = .CreateEventProc("Click", "BtnExport") + 1
.InsertLines lLine, "Call CSVFile"
End With
oXl.EnableEvents = True
oXl.DisplayAlerts = True
Exit Sub
errtrap:
End Sub
An easy way to do it:
1 - Insert a class module and write this code:
Public WithEvents ChkEvents As MSForms.CommandButton
Private Sub ChkEvents_click()
MsgBox ("Click Event")
End Sub
2 - Insert a userform and write this code:
Dim Chk As New Clase1
Private Sub UserForm_Initialize()
Dim NewCheck As MSForms.CommandButton
Set NewCheck = Me.Controls.Add("Forms.CommandButton.1")
NewCheck.Caption = "Prueba"
Set Chk.ChkEvents = NewCheck
End Sub
Now show the form and click the button
I think the code needs to be added to the Userform, not to the button itself.
So something like
With UserForm1.CodeModule
'Insert code here
End With
In place of your With ThisWorkbook

Resources