I am trying to do textboxes which would be validating input (for numbers only).
I am still new to classes and am little bit confused about some things, but trying my best to learn.
I have multiple textboxes in userform and want to every one of them to be numeric input only.
For the begining I started to check just one textbox (vzdalenost1).
First code just to create connection between textbox and class
Dim chk As New Class1
Private Sub UserForm_Initialize()
Set chk.ChkEvents = Controls("Vzdalenost1")
End Sub
Second code is actual class module
Option Explicit
Public WithEvents ChkEvents As MSForms.TextBox
Private Sub ChkEvents_change()
If IsNumeric(Me.Value) Or Me.Value = "" Then
Else
MsgBox "blablabla"
Me.Value = ""
End If
End Sub
When I try to write something into textbox "vzdalenost1" excel shows error message "Method or data member not found"..
I have even something like replacing "me.value" for "me.control.value" which didnt work either..
The keyword Me in this context refers to the class object itself, not the textbox. Use ChkEvents to refer to the textbox - ChkEvents.Value.
Here's how you could re-write your code to deal with multiple textboxes. First, let's give your class a meaningful name, let's call it clsTextbox. Then, in keeping with the principle of encapsulation, let's declare the object in your class as Private, instead of Public. This will prevent the public from having direct access to it. Instead, we'll use a member function to assign a textbox to a class object. So the code for our class module would look like something this...
Option Explicit
Private WithEvents tb As MSForms.TextBox
Private Sub tb_Change()
MsgBox tb.Name
End Sub
Public Function SetTextbox(ByRef obj As MSForms.TextBox)
Set tb = obj
End Function
Then, for our userform, first we declare a collection at the module level to hold our class objects. Then, since we will be creating multiple class objects, we declare a class object without the keyword New. Instead, we'll use that keyword each time we create a new object, and then we'll add that new object to our collection. So the code for our userform would look something like this...
Option Explicit
Dim textboxCollection As Collection
Private Sub UserForm_Initialize()
Set textboxCollection = New Collection
Dim cTextbox As clsTextbox
Dim ctrl As MSForms.Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "TextBox" Then
Set cTextbox = New clsTextbox
cTextbox.SetTextbox ctrl
textboxCollection.Add cTextbox
End If
Next ctrl
End Sub
If I get your question correctly you want a textbox to accept only numbers, correct?
In that case try this:
Private Sub TextboxName_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
char = Chr(KeyAscii)
KeyAscii = Asc(UCase(char))
Debug.Print KeyAscii
If KeyAscii >= 48 And KeyAscii <= 57 Then
Debug.Print "number"
Else
Debug.Print "other"
KeyAscii = 0
End If
End Sub
Just replace TextboxName in the Sub name with whatever name you have and it should work.
Related
I wrote some code in a class module and userform_initialize_event. All are okay. But when I Dim the new class module and applies in the initialize event it says that variable not defined. Here is my code -
Dim Buttons() As New BtnClass
Private Sub UserForm_Initialize()
Dim ButtonCount As Integer
Dim ctl As Control
' Create the Button objects
ButtonCount = 0
For Each ctl In fmHover.Controls
If TypeName(ctl) = "Label" Then
ButtonCount = ButtonCount + 1
ReDim Preserve Buttons(1 To ButtonCount)
Set Buttons(ButtonCount).ButtonGroup = ctl
End If
Next ctl
End Sub
If I Dim the Buttons inside the event it does not throws any error and the code also don't works. I searched in a lot of place. Every one Dim the new class before the Initialize event. So, why my one doesn't work? Kindly suggest where is my mistake.
Here is my class module code -
Public WithEvents ButtonGroup As MSForms.Label
Private Sub ButtonGroup_Click()
Msg = "You clicked " & ButtonGroup.Name
MsgBox Msg
ButtonGroup.Name
End Sub
Thanks in advance.
Each label on your form should have it's own instance of the class. The form needs a way to remember each instance - stored in a collection at the form level.
Not sure what the ButtonCount is doing, so have removed it.
Your form code:
Option Explicit '!!!Add option explicit to the top of each module!!!
'It helps avoid so many errors that I don't think three
'exclamations marks is enough!!!!
'>Tools>Options>tick Require variable declaration
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Each instance of the class is stored here.
Private colLabels As Collection
Private Sub UserForm_Initialize()
Dim BtnEvents As BtnClass
'Initialise the collection to hold the class instances.
Set colLabels = New Collection
Dim ctrl As Control
For Each ctrl In Me.fmHover.Controls
If TypeName(ctrl) = "Label" Then
Set BtnEvents = New BtnClass 'New instance of the class.
Set BtnEvents.AssignButton = ctrl 'Assign the label to it.
colLabels.Add BtnEvents 'Add it to the collection so it's remembered.
End If
Next ctrl
End Sub
Your class module will look like:
Option Explicit 'More exclamation marks!!!!!!!!!
Public WithEvents ButtonGroup As MSForms.Label
'Let the class know what control it's assigned to.
Public Property Set AssignButton(ctrl As Control)
Set ButtonGroup = ctrl
End Property
Private Sub ButtonGroup_Click()
Dim Msg As String
Msg = "You clicked " & ButtonGroup.Name
MsgBox Msg
End Sub
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
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
I've inserted a user form into a project that already contains a bunch of modules. By playing around with the code in the user form, I've verified that I can return the value from a combo box.
User form code:
Public SelectedPacking As Integer
Private Sub CancelButton_Click()
UserForm1.Hide
End Sub
Private Sub OKButton_Click()
SelectedPacking = ComboBox1.ListIndex
Call DemoDialogOk
'Return list index value to cell C50
Worksheets("Inputs & Results").Range("C50") = SelectedPacking
Unload UserForm1
End Sub
My problem is that I can't pass this value on to any of the macros written in the modules.
Module code:
Public Sub ShowComboBox()
UserForm1.Show
End Sub
Public Sub DemoDialogOk()
ival = SelectedPacking
'Return value of ival (list index value from combo box) to cell C17
Worksheets("Packed bed (Random)").Range("C17") = ival
End Sub
Obviously the module contains more useful code, but I've commented out everything to try and figure out where I'm going wrong. I've been changing some things around, but I still can't get anything to appear in cell C17, so I think I'm missing something fundamental.
I think two options: 1) change DemoDialogueOK to accept variables:
Public Sub DemoDialogOk(SelPack as integer)
' ival = SelectedPacking
Worksheets("Packed bed (Random)").Range("C17") = SelPack
End Sub
Private Sub OKButton_Click()
SelectedPacking = ComboBox1.ListIndex
Call DemoDialogOk(SelectedPacking)
...
End Sub
Or option two: fully qualify the variable from the useform i.e:
Public Sub DemoDialogOk()
ival = ufYourForm.SelectedPacking
...
End Sub
Public variables in userforms don't appear to be as "public" as module level...
Tipping on top of Simon's answer, you could pass the entire userform if you'd like. This would give you access to all the pieces of it and is especially useful if you need to do some validation on, say, different checkboxes being checked or not.
Sub inOurUserForm()
Call inADifferentModule(Me) 'Passes this userform
End Sub
Sub inADifferentModule(ourForm As UserForm1)
'Passed the form, and using it like a class (As whatever the form is called)
If ourForm.chkYes = True Then
'Do something
Else
'Do something else, like
ourForm.chkYes = False 'Because we are passing the object itself _
rather than a copy, at least in my understanding
End If
End Sub
And you don't necessarily need to pass the userform, as you could just reference it as an object itselft e.g.
UserForm1.chkYes
A very easy solution to this would be to declare a variable within the Userform (UserForm1 in this example)
Public Pass as string
This Pass would contain the string where you store the password. Once you store the password, you can hide the form
Me.Hide
Within the module, you can open the Form as modal
UserForm1.Show vbModal
Now after all the code inside the userform is run, the password can be retrieved within the module -
UserForm1.Pass
You can then unload the hidden form
unload UserForm1
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.