I have multiple buttons (active x) on a spreadhseet in same column but different rows. These buttons capture the start time of an activity.
If button 1 is pressed cell next to it should be populated by current time.
If button 2 is pressed cell next to it should be populated by current time. and so on.....
I have written a SUB in VBA as follows:
Private Sub StartTimer_Click()
Range("I4").Value = Now
End Sub
I do not want to repeat this code for each button action. Please let me know how it can be made dynamic.
A simple WithEvents example:
in a class (named clsButtons):
Private WithEvents Bt As MSForms.CommandButton
Property Set obj(b As MSForms.CommandButton)
Set Bt = b
End Property
Private Sub Bt_Click()
'uses the right of the name of the CommandButton
Cells(1 + Right(Bt.Name, 1) * 3, 9).Value = Now
End Sub
In the sheetcode (the one with the buttons):
Dim myButtons As Collection
Private Sub Worksheet_Activate()
Dim ctl As OLEObject
Dim ButtonClass As clsButtons
Set myButtons = New Collection
For Each ctl In Sheet1.OLEObjects
If ctl.progID = "Forms.CommandButton.1" Then
Set ButtonClass = New clsButtons
Set ButtonClass.obj = ctl.Object
myButtons.Add ButtonClass
End If
Next ctl
End Sub
Create a standard module and put the procedure in there.
While it is possible to share a procedure in a private module, it's best practice to put any shared procedures in a shared module.
In the VBA Editor click Insert > Module,
Paste into there, and give it a unique name. Using your example you could do something like:
Public Sub SetTimeValue()
Range("I4").Value = Now
End Sub
...then call this public stub from your other one, like:
Private Sub StartTimer_Click()
SetTimeValue
End Sub
...and from any other locations where you need to call your code.
I assume that you have more than one line of code for the actual procedure you're concerned about, otherwise copying it repeatedly isn't really a concern.
More Information:
MSDN : Understanding Scope and Visibility
Office Support : Scope of variables in Visual Basic for Applications
Chip Pearson : Understanding Scope Of Variables And Procedures
PowerSpreadsheets : Excel VBA Sub Procedures: The Complete Tutorial
MVP : Cut out repetition using subs and functions with arguments
Related
Hi I am trying to use a publicly declared variable that has a value assigned to it upon clicking one of two optionbuttons inside a userform.
The userform does show and I can click either of the two buttons i have in the frame I created, but the variable does not pick up the click or actually I don't know what I'm doing with the userform controls. And i don't think the form hides either, clicking the optionbutton seems to be a dummy click, it highlights but does nothing after that.
Plus i'm not sure if the code itself needs to be placed on the userform' code section or the module code section, does it make a difference? Currently running it on the module code section.
Option Explicit
Dim C As String
Public Sub OptionButton1_Click()
C = "Delivery"
End Sub
Public Sub OptionButton2_Click()
C = "Holiday"
End Sub
Public Sub Optionbutton()
Userform1.Show
Sheet1.Cells(1, 1).Value = C
Userform1.Hide
End Sub
********--Edit-*********
I guess Zack's method gets the job done, printing to Cell A1, but what I was really after is although the event handler needs to be private, running debug points out that the execution stops when the private sub ends, which could be optionbutton1 or optionbutton2's click event, which is what led me to believe the click was a dummy click.
But what I was really after is for the C variable to be available in the public sub that I execute from, is there a way for the execution to divert to the public sub after the C is assigned a value in the optionbutton private sub?
For example if the C was assigned in the private sub but my success criteria were defined by the successful execution of the public Sub defined above, because i have a larger macro where that variable will be used inside the public sub, and this is only part of the problem, or the problem I can't solve.
If I understand correctly, your form has two option buttons. Thus, your form's code-behind should look like this:
Option Explicit
Private Sub OptionButton1_Click()
C = "Delivery"
End Sub
Private Sub OptionButton2_Click()
C = "Holiday"
End Sub
This assumes there's a global variable C declared somewhere, in a standard module:
Option Explicit
Public C As String
Public Sub Optionbutton()
Userform1.Show
Sheet1.Cells(1, 1).Value = C
End Sub
Note that this displays the form modally, which means the next instruction only runs after the form is closed/destroyed. For that reason, there's no need for the subsequent Userform1.Hide call: the form is already gone by then.
Once you're comfortable with manipulating global variables from a form's code-behind, read UserForm1.Show (I wrote that article) for insight on a vastly more robust way to go about this.
Try this:
You will need to tell the control where to place the value after the button is clicked.
Option Explicit
Dim C As String
Private Sub OptionButton1_Click()
C = "Delivery"
Sheet1.Cells(1, 1) = C
Userform1.Hide
End Sub
Private Sub OptionButton2_Click()
C = "Holiday"
Sheet1.Cells(1, 1) = C
Userform1.Hide
End Sub
Public Sub Optionbutton()
Userform1.Show vbModeless
As the subject suggest, I have build a macro that will need a variable input
Sub GetFile(Account as String)
Currently i am setting multiple sub to call the script and assign them to different command buttons
Sub AC1_File
Call GetFile("Account1")
end sub
Sub AC2_File
Call GetFile("Account2")
end sub
And the list go on
I am trying to not make my code too long (as i got double digits accounts),
is it possible for me code to get properties of the commandbutton name and use it instead of Account1,2,3...?
Something like below?
Call GetFile(triggering-commandbutton.name)
Form Controls
In a normal module add a procedure with no arguments:
Public Sub Button_Click()
GetFile Application.Caller
End Sub
This code will pick up the button name and pass it to the GetFile procedure.
Sub GetFile(Account As String)
MsgBox "Account Name is " & Account
End Sub
So now all you need to do is name your buttons Account1, Account2, etc..
ActiveX Controls
If you're using ActiveX controls you can use a class to capture the click event.
Create a class module and name it clsButtonClick.
Add this code to the class module:
Public WithEvents AccountBtn As MSForms.CommandButton
Private Sub AccountBtn_Click()
MsgBox AccountBtn.Name & " on " & AccountBtn.Parent.Name
GetFile AccountBtn.Name
End Sub
At the very top of a normal module add this line:
Public colBtns As New Collection
And add this code to the module:
Public Sub Initialize_Buttons()
Dim wrkSht As Worksheet
Dim btnEvnt As clsButtonClick
Dim obj As OLEObject
For Each wrkSht In ThisWorkbook.Worksheets
For Each obj In wrkSht.OLEObjects
If TypeName(obj.Object) = "CommandButton" Then
Set btnEvnt = New clsButtonClick
Set btnEvnt.AccountBtn = obj.Object
colBtns.Add btnEvnt
End If
Next obj
Next wrkSht
End Sub
This will go through each sheet in your workbook and give any ActiveX command buttons the click event from the class module.
Update the Initialize_Buttons procedure if you need to limit it to a specific sheet, or specific buttons on a sheet.
Finally call the Initialize_Buttons code in the Workbook_Open event in the ThisWorkbook module.
You can create many commandbuttons as ActiveX controller, then write VBA like below:
Private Sub CommandButton1_Click()
Call GetFile("Account1")
End Sub
Private Sub CommandButton2_Click()
Call GetFile("Account2")
End Sub
I have a user-form which is made up of many subs, this is assigned as a macro to a button on the worksheet. When the user is finished with this user-form they can press a button on it which causes its visibility to become false and when entered again everything appears how it was left resulting in a save like feature.
I now need to apply this to multiple buttons on the worksheet and each user form needs to have the exact same code and same buttons but be a separate form as each individual button requires it's own save like feature. The way I was planning on doing this was to copy the existing user form and paste it many times with different names however, if a modification is required it will take a long time to carry out therefore, is there a method such as "include" which could use a base module from which all the code is accessed so that if I ever need to change anything I just do it on that one module and everything else updates via the include?
EDIT:
I now have a public function called costing() and am getting an error when I used:
Private Sub material_Change()
Call costing
End Sub
You can have multiple instances of the same form. You can use this to retain multiple sets of form values
Try this:
Create your form, as usual. Let's call it MyForm
Create several buttons on your sheet. My example uses ActiveX buttons, but Form Control buttons can be used too. Let's call them CommandButton1 and CommandButton2
In your form module, include a Terminate Sub, which includes this code
Private Sub UserForm_Terminate()
' any other code you may need...
Unload Me
End Sub
The Form buton to save/Hide the form needs to be
Private Sub btnSaveAndHide_Click()
Me.Hide
End Sub
The Sheet Button code is as follows
The code is identical for each button (and calls a common Sub), and each button has its own Static form variable.)
The Error handler is needed to deal with the case a form is not properly closed. In this case the instance no longer exists, but the local Static variable is also not Nothing
Example shows form shown as Modeless, you can change this to Modal if you want.
Private Sub CommandButton1_Click()
Static frm As MyForm
ShowMyForm frm
End Sub
Private Sub CommandButton2_Click()
Static frm As MyForm
ShowMyForm frm
End Sub
Private Sub ShowMyForm(frm As MyForm)
If frm Is Nothing Then Set frm = New MyForm
On Error GoTo EH
frm.Show vbModeless
Exit Sub
EH:
If Err.Number = -2147418105 Then
On Error GoTo 0
Set frm = Nothing
Set frm = New MyForm
frm.Show
End If
On Error GoTo 0
End Sub
End result: multiple copies of the same form, each with their own values
In responce to comment How would I access the variables inside of each user form externally
In the example above the Form instances are only accessable in the Command Button Click Handler routines, or within the Form module itself. If you can write your code in the form module, then no change is needed.
To make the Form instances available elsewhere, consider moving their declaration to Module Scope of a standard Module. You could declare them as, eg individual variables, an array (either static or dynamic), a Collection, a Dictionary. Which structure is best will depend on how you want to manage and access your form instances.
For example, a Static Array: Code in a standard Module
Option Explicit
Global MyForms(1 To 2) As MyForm
Update the CommandButton code to
Private Sub CommandButton1_Click()
ShowMyForm Module1.MyForms(1)
End Sub
Private Sub CommandButton2_Click()
ShowMyForm Module1.MyForms(2)
End Sub
Private Sub ShowMyForm(frm As MyForm) no change, same as before
The code works the same as before, but you can now access the Global variable in a standard Module
Sub Demo()
Dim i As Long
For i = LBound(MyForms) To UBound(MyForms)
If Not MyForms(i) Is Nothing Then
MsgBox "Form " & i & " Value = " & MyForms(i).TextBox1.Value
End If
Next
End Sub
You don't need an "Include" (none exists in VBA); all you need to do is create a module and make the common methods public.
For example, if you create a module and have a function like this:
Public Function Add(first As Integer, second As Integer) As Integer
Add = first + second
End Function
Then you can access it like this from another module/form/class module:
Sub test()
MsgBox Add(3, 6)
End Sub
I have a thousands of cells in an Excel worksheet which are ComboBoxes. The user will select one at random and populate it.
How do I get the selected ComboBox value? Is there a way to trigger a function (i.e. an event handler) when the ComboxBoxes has been selected?
You can use the below change event to which will trigger when the combobox value will change.
Private Sub ComboBox1_Change()
'your code here
End Sub
Also you can get the selected value using below
ComboBox1.Value
If you're dealing with Data Validation lists, you can use the Worksheet_Change event. Right click on the sheet with the data validation and choose View Code. Then type in this:
Private Sub Worksheet_Change(ByVal Target As Range)
MsgBox Target.Value
End Sub
If you're dealing with ActiveX comboboxes, it's a little more complicated. You need to create a custom class module to hook up the events. First, create a class module named CComboEvent and put this code in it.
Public WithEvents Cbx As MSForms.ComboBox
Private Sub Cbx_Change()
MsgBox Cbx.Value
End Sub
Next, create another class module named CComboEvents. This will hold all of our CComboEvent instances and keep them in scope. Put this code in CComboEvents.
Private mcolComboEvents As Collection
Private Sub Class_Initialize()
Set mcolComboEvents = New Collection
End Sub
Private Sub Class_Terminate()
Set mcolComboEvents = Nothing
End Sub
Public Sub Add(clsComboEvent As CComboEvent)
mcolComboEvents.Add clsComboEvent, clsComboEvent.Cbx.Name
End Sub
Finally, create a standard module (not a class module). You'll need code to put all of your comboboxes into the class modules. You might put this in an Auto_Open procedure so it happens whenever the workbook is opened, but that's up to you.
You'll need a Public variable to hold an instance of CComboEvents. Making it Public will kepp it, and all of its children, in scope. You need them in scope so that the events are triggered. In the procedure, loop through all of the comboboxes, creating a new CComboEvent instance for each one, and adding that to CComboEvents.
Public gclsComboEvents As CComboEvents
Public Sub AddCombox()
Dim oleo As OLEObject
Dim clsComboEvent As CComboEvent
Set gclsComboEvents = New CComboEvents
For Each oleo In Sheet1.OLEObjects
If TypeName(oleo.Object) = "ComboBox" Then
Set clsComboEvent = New CComboEvent
Set clsComboEvent.Cbx = oleo.Object
gclsComboEvents.Add clsComboEvent
End If
Next oleo
End Sub
Now, whenever a combobox is changed, the event will fire and, in this example, a message box will show.
You can see an example at https://www.dropbox.com/s/sfj4kyzolfy03qe/ComboboxEvents.xlsm
A simpler way to get the selected value from a ComboBox control is:
Private Sub myComboBox_Change()
msgbox "You selected: " + myComboBox.SelText
End Sub
Maybe you'll be able to set the event handlers programmatically, using something like (pseudocode)
sub myhandler(eventsource)
process(eventsource.value)
end sub
for each cell
cell.setEventHandler(myHandler)
But i dont know the syntax for achieving this in VB/VBA, or if is even possible.
I have a multipage, I was successfully able to copy elements of the first page which is my reference page to new pages which is created dynamically.
My question is, how do I set a commandbutton's actions inside a page in a multipage control?
My goal is to click on the button from any page then pops up another form.
How do I do this?
It's pretty hard to adjust from Android to VB. I really appreciate any help from you guys.
This is my code in cloning pages.
i = 0
MultiPage1.Pages.Add
MultiPage1.Pages(i).Controls.Copy
i = i + 1
MultiPage1.Pages(i).Paste
For Each ctl In Me.MultiPage1.Pages(i).Controls
If TypeOf ctl Is MSForms.Label Then
'~~~ code omitted
Select Case ctl.Tag
Case "startTime"
ctl.Caption = "4:00pm"
End Select
End If
Next
this is how it's going to look like.
the button will concatenate all strings inside the page. the concatenated string will be shown on another userform.
You probably would be better off creating a button in the ribbon so that it is available on all pages:
http://chandoo.org/wp/2012/02/27/how-to-add-your-own-macros-to-excel-ribbon/
EDIT:
My bad, I thought you meant a worksheet instead of a VBA MultiPage in a userform.
Check this out. I was able to make this work for me:
Assign code to a button created dynamically
Class1:
Option Explicit
Public WithEvents CmdEvents As MSForms.CommandButton
Private Sub CmdEvents_Click()
MsgBox "yo"
End Sub
Userform with MultiPage object:
Option Explicit
Dim cmdArray() As New Class1
Private Sub CommandButton1_Click()
Dim newControl As Control
Set newControl = Me.MultiPage1.Pages(0).Controls.Add("Forms.CommandButton.1", "NewCommand", True)
newControl.Object.Caption = "hello"
newControl.Left = 50
newControl.Top = 50
ReDim Preserve cmdArray(1 To 1)
Set cmdArray(1).CmdEvents = newControl
Set newControl = Nothing
End Sub
You can do this with a custom class. The class basically has one member Public WithEvents b as CommandButton.
The trick is the WithEvents keyword. You can now insert some code to generically handle the click of a button that is assigned to this class:
Private Sub b_Click()
MsgBox "You clicked " & b.Name 'Modify this event so that different code is executed base on the page/name/etc.
End Sub
In order to make this work, you need to assign the button you create in your code to an object of this new class:
Private objButtonHandler as New MyClass 'this should be scope a UserForm wide
Sub YourSub
Dim objYourButton as CommandButton
Set objYourButton = ... `your code here
Set objButtonHandler.b = objYourButton
End Sub