I am experimenting with the capabilities of a User Form to assist in Data entry. I would like to know if there is a specific code that can transfer the Value of a Checkbox to a Text box that is on the same User Form when the box is checked. Basically, instead of having to type all of the words out, it would be easier to simply check a series of boxes to create the sentence. I know how to input the Checkbox value into the Excel worksheet but I have yet to figure out how to have that same value entered into a Text Box that would provide a "Preview" of the sentence for editing purposes and then the data can be transferred to the Excel Worksheet once it has been approved in the User Form. In my attached example that I had created and I have a before and the desired after result of what I am looking to do.
Thank you
JLY Test form:
EDIT:
Though really, you'd be best off using a listbox, much easier and shorter code. Put your list of items in a worksheet and set that to a dynamic named range so you can edit it and the userform will pick it up on the fly. Make sure the listbox has a MultiSelect property set to fmMultiSelectExtended and the ListStyle property is set to fmListStyleOption. Then you can select multiple entries in the listbox by holding the Ctrl key.
In this example if have put it in Sheet1 (though can be any sheet, and the sheet can even be hidden), and then set a dynamic named range named listProperties to this formula: =Sheet1!$A$2:INDEX(Sheet1!$A:$A,MAX(2,ROWS(Sheet1!$A:$A)-COUNTBLANK(Sheet1!$A:$A)))
Then the userform has this code:
Private Sub listJewelryProperties_Change()
Dim sTemp As String
Dim i As Long
For i = 0 To Me.listJewelryProperties.ListCount - 1
If Me.listJewelryProperties.Selected(i) = True Then sTemp = sTemp & " " & Me.listJewelryProperties.List(i)
Next i
Me.txtPreview.Text = WorksheetFunction.Trim(sTemp)
End Sub
Private Sub UserForm_Initialize()
Me.listJewelryProperties.Clear
Me.listJewelryProperties.List = ActiveWorkbook.Sheets("Sheet1").Range("listProperties").Value
End Sub
And this is what the results look like:
Original Answer Here for Posterity:
Alternate solution, set all your checkboxes to call a function and pass an argument of their value and caption, then the function will update the textbox. The reason for using the .Tag property is to avoid removing too much due to duplicates in partial matches for the checkboxes (such as Ring and Ring Band where just plain Ring can be found within Ring Band, this way it will only remove the Ring entry, and not incorrectly remove both Ring entries)
Private Sub chk14ktWhiteGold_Click()
UpdatePreview Me.chk14ktWhiteGold.Value, Me.chk14ktWhiteGold.Caption
End Sub
Private Sub chkAntique_Click()
UpdatePreview Me.chkAntique.Value, Me.chkAntique.Caption
End Sub
Private Sub chkArtisinal_Click()
UpdatePreview Me.chkArtisinal.Value, Me.chkArtisinal.Caption
End Sub
Private Sub chkBand_Click()
UpdatePreview Me.chkBand.Value, Me.chkBand.Caption
End Sub
Private Sub chkHandCarved_Click()
UpdatePreview Me.chkHandCarved.Value, Me.chkHandCarved.Caption
End Sub
Private Sub chkHandEtched_Click()
UpdatePreview Me.chkHandEtched.Value, Me.chkHandEtched.Caption
End Sub
Private Sub chkHandmade_Click()
UpdatePreview Me.chkHandmade.Value, Me.chkHandmade.Caption
End Sub
Private Sub chkRing_Click()
UpdatePreview Me.chkRing.Value, Me.chkRing.Caption
End Sub
Private Sub chkRingBand_Click()
UpdatePreview Me.chkRingBand.Value, Me.chkRingBand.Caption
End Sub
Private Sub chkSterlingSilver_Click()
UpdatePreview Me.chkSterlingSilver.Value, Me.chkSterlingSilver.Caption
End Sub
Private Sub chkVintage_Click()
UpdatePreview Me.chkVintage.Value, Me.chkVintage.Caption
End Sub
Private Sub UpdatePreview(ByVal bChkState As Boolean, ByVal arg_sText As String)
If bChkState = True Then
Me.txtPreview.Text = WorksheetFunction.Trim(Me.txtPreview.Text & " " & arg_sText)
If Len(Me.txtPreview.Tag) = 0 Then
Me.txtPreview.Tag = arg_sText
Else
Me.txtPreview.Tag = Me.txtPreview.Tag & "|" & arg_sText
End If
Else
Me.txtPreview.Tag = Replace("|" & Me.txtPreview.Tag & "|", "|" & arg_sText & "|", "|")
Me.txtPreview.Text = WorksheetFunction.Trim(Replace(Me.txtPreview.Tag, "|", " "))
End If
End Sub
Something like below will do that for you, as I don't know the names of your UserForm or your Checkboxes, you will have to amend as required, also you will have to add the following code to each of the CheckBoxes Click Event:
Private Sub CheckBox1_Click()
UserForm1.TextBox1.Text = UserForm1.TextBox1.Text & " " & CheckBox1.Caption
End Sub
UPDATE:
To also remove when checkbox is unchecked, the following code will do that:
Private Sub CheckBox1_Click()
If CheckBox1.Value = True Then
UserForm1.TextBox1.Text = UserForm1.TextBox1.Text & " " & CheckBox1.Caption
Else
pos = InStr(UserForm1.TextBox1.Text, CheckBox1.Caption)
If pos > 0 Then UserForm1.TextBox1.Text = Replace(UserForm1.TextBox1.Text, " " & CheckBox1.Caption, "")
End If
End Sub
Related
I'm trying to reduce repetition in my Userform Code. I've tried writing a sub that sets a SpinButton and Textbox values equal to a defined default value. How do I pass the Spinbutton/Textbox as parameters in a private sub?
Referring to them directly brings up a type mismatch error.
Here's the code that doesn't work:
Private Sub UserForm_Initialize()
Call Default_Setter(TextBox_1, SpinButton_1, 0)
End Sub
Private Sub Default_Setter(InputTextBox As TextBox, InputSpinButton As SpinButton, InputDefaultValue As Integer)
InputTextBox.Locked = True
InputSpinButton.Value = InputDefaultValue
InputTextBox.Text = InputSpinButton.Value
InputTextBox.ControlTipText = "Value between " & InputSpinButton.Min & " and " & InputSpinButton.Max
End Sub
I am using Excel 2003 with VBA, I am dynamically creating check box controls on a sheet and want to link the VBA controls to a class so that when a user clicks on a checkbox an event is fired so I can do something.
From what I've read it would seem that creating a user class is the solution, but having tried this I can't get it to work.
My user class looks like this:
Option Explicit
Public WithEvents cbBox As MSForms.checkbox
Private Sub cbBox_Change()
MsgBox "_CHANGE"
End Sub
Private Sub cbBox_Click()
MsgBox "_CLICK"
End Sub
My code to create the checkboxes:
For Each varExisting In objColumns
'Insert the field name
objColumnHeadings.Cells(lngRow, 1).Value = varExisting
'Insert a checkbox to allow selection of the column
Set objCell = objColumnHeadings.Cells(lngRow, 2)
Dim objCBclass As clsCheckbox
Set objCBclass = New clsCheckbox
Set objCBclass.cbBox = ActiveSheet.OLEObjects.Add( _
ClassType:="Forms.CheckBox.1" _
, Left:=300 _
, Top:=(objCell.Top + 2) _
, Height:=10 _
, Width:=9.6).Object
objCBclass.cbBox.Name = "chkbx" & lngRow
objCBclass.cbBox.Caption = ""
objCBclass.cbBox.BackColor = &H808080
objCBclass.cbBox.BackStyle = 0
objCBclass.cbBox.ForeColor = &H808080
objCheckboxes.Add objCBclass
lngRow = lngRow + 1
Next
The checkboxes are visible in the sheet, but when I click on them, no message box is displayed so the link to the class doesn't seem to be working.
Why?
Edit...If after adding the checkboxes I go into the VB IDE and select one of the created checkboxes from the list of controls, then select Click from the Procedure drop down list, it will insert the code for a call back which if I add a message box to this, works when I click on the same checkbox...so how can I achieve this in code? I've tried recording a macro to do this, nothing was recorded.
Edit by S.Platten, jump to the bottom for how this helped me fix the problem...
Due to some weird reason, VBA doesn't hook up the events for Sheet's ActiveX control in the same execution cycle in which they were added. So, we need to come out of the cycle which added the controls and then invoke the event adding proc in next cycle. Application.OnTime helps here.
Its seems a bit of overkill but it works :)
Option Explicit
Dim collChk As Collection
Dim timerTime
Sub master()
'/ Add the CheckBoxes First
Call addControls
'<< Due to some weird reason, VBA doesn't hook up the events for Sheet's ActiveX control in the same
'execution cycle in which they were added. So, we need to come out of the cycle which added the controls
'and then invoke the event adding proc in next cycle. >>
'/ Start Timer. Timer will call the sub to add the events
Call StartTimer
End Sub
Sub addControls()
Dim ctrlChkBox As MSForms.CheckBox
Dim objCell As Range
Dim i As Long
'Intialize the collection to hold the classes
Set collChk = New Collection
'/ Here Controls are added. No Events, yet.
For i = 1 To 10
Set objCell = Sheet1.Cells(i, 1)
Set ctrlChkBox = Sheet1.OLEObjects.Add( _
ClassType:="Forms.CheckBox.1" _
, Left:=1 _
, Top:=(objCell.Top + 2) _
, Height:=objCell.Height _
, Width:=100).Object
ctrlChkBox.Name = "chkbx" & objCell.Row
Next
End Sub
Sub addEvents()
Dim ctrlChkBox As MSForms.CheckBox
Dim objCBclass As clsCheckBox
Dim x As Object
'Intialize the collection to hold the classes
Set collChk = New Collection
'/ Here we assign the event handler
For Each x In Sheet1.OLEObjects
If x.OLEType = 2 Then
Set ctrlChkBox = x.Object
Set objCBclass = New clsCheckBox
Set objCBclass.cbBox = ctrlChkBox
collChk.Add objCBclass
Debug.Print x.Name
End If
Next
'/ Kill the timer
Call StopTimer
End Sub
Sub StartTimer()
timerTime = Now + TimeSerial(0, 0, 1)
Application.OnTime EarliestTime:=timerTime, Procedure:="addEvents", _
Schedule:=True
End Sub
Sub StopTimer()
On Error Resume Next
Application.OnTime EarliestTime:=timerTime, Procedure:="addEvents", _
Schedule:=False
End Sub
Class Module: clsCheckBox
Option Explicit
Public WithEvents cbBox As MSForms.CheckBox
Private Sub cbBox_Change()
MsgBox "_CHANGE"
End Sub
Private Sub cbBox_Click()
MsgBox "_CLICK"
End Sub
Edit continued...
The class (clsCheckbox):
Option Explicit
Public WithEvents cbBox As MSForms.checkbox
Private Sub cbBox_Click()
MsgBox "_CLICK"
End Sub
Module1
Public objCheckboxes As Collection
Public tmrTimer
Public Sub addEvents()
Dim objCheckbox As clsCheckbox
Dim objMSCheckbox As Object
Dim objControl As Object
Set objCheckboxes = New Collection
For Each objControl In Sheet1.OLEObjects
If objControl.OLEType = 2 _
And objControl.progID = "Forms.CheckBox.1" Then
Set objMSCheckbox = objControl.Object
Set objCheckbox = New clsCheckbox
Set objCheckbox.cbBox = objMSCheckbox
objCheckboxes.Add objCheckbox
End If
Next
Call stopTimer
End Sub
Public Sub startTimer()
tmrTimer = Now + TimeSerial(0, 0, 1)
Application.OnTime EarliestTime:=tmrTimer _
, Procedure:="addEvents" _
, Schedule:=True
End Sub
Public Sub stopTimer()
On Error Resume Next
Application.OnTime EarliestTime:=tmrTimer _
, Procedure:="addEvents" _
, Schedule:=False
End Sub
The code in the sheet that adds the controls:
Dim objControl As MSForms.checkbox
For Each varExisting In objColumns
'Insert the field name
objColumnHeadings.Cells(lngRow, 1).Value = varExisting
'Insert a checkbox to allow selection of the column
Set objCell = objColumnHeadings.Cells(lngRow, 2)
Set objControl = ActiveSheet.OLEObjects.Add( _
ClassType:="Forms.CheckBox.1" _
, Left:=300 _
, Top:=(objCell.Top + 2) _
, Height:=10 _
, Width:=9.6).Object
objControl.Name = "chkbx" & lngRow
objControl.Caption = ""
objControl.BackColor = &H808080
objControl.BackStyle = 0
objControl.ForeColor = &H808080
lngRow = lngRow + 1
Next
This isn't the entire project, but enough to demonstrate the workings.
You are currently using ActiveX controls. Yet, ActiveX controls are bound to specific naming conventions. For example: if you insert an ActiveX button onto a sheet and name it btnMyButton then the sub must be named btnMyButton_Click. The same applies to checkboxes. If you insert a new checkbox with the name CheckBox2 then the sub's name must be CheckBox2_Click. In short, there cannot be a sub with the name cbBox_Change associated to any ActiveX checkbox.
So, what you really need (with ActiveX controls) is a way to change the VBA code on a sheet. But thus far I have never come across any such code (VBA code to change VBA code on a sheet).
A much easier route would be if you'd be willing to use form controls instead.
The following sub will create a (form control) checkbox and assign the macro tmpSO to it. The sub tmpSO (unlike subs for ActiveX controls) does not need to reside on the sheet but can be in any module.
Sub Insert_CheckBox()
Dim chk As CheckBox
Set chk = ActiveSheet.CheckBoxes.Add(390.75, 216, 72, 72)
chk.OnAction = "tmpSO"
End Sub
Since a from control is calling the sub tmpSO you can use Application.Caller in that sub and thereby know which checkbox has been calling this sub.
Sub tmpSO()
Debug.Print Application.Caller
End Sub
This will return the name of the CheckBox. So, you can use this one sub for all of your checkboxes any dynamically handle them based on their names (possibly using a Case Select).
Here is another example for tmpSO:
Sub tmpSO()
With ThisWorkbook.Worksheets(1).CheckBoxes(Application.Caller)
MsgBox "The checkbox " & Application.Caller & Chr(10) & _
"is currently " & IIf(.Value = 1, "", "not") & " checked."
End With
End Sub
basically I have a userform which I would like to use to enter 2 data into another macro which I already have. The userform is as below:
Basically, I would like the OK button to be clicked and the data in the two boxes will be entered into another macro that I have. It would also be great if the OK button can help in a sense that it will prompt a warning if one of the boxes is not filled up.
So far, I do not have much of a code for this..
Private Sub UserForm_Click()
TextBox1.SetFocus
Sub Enterval()
End Sub
Private Sub TextBox1_Change()
Dim ID As String
ID = UserForm3.TextBox1.Value
End Sub
Private Sub TextBox2_Change()
Dim ID2 As String
ID2 = UserForm3.TextBox2.Value
End Sub
Private Sub OKay_Click()
Enterval
End Sub
Would appreciate any tips and help. Thanks!
My other macro
Private Sub CommandButton1_Click()
Dim Name As String
Dim Problem As Integer
Dim Source As Worksheet, Target As Worksheet
Dim ItsAMatch As Boolean
Dim i As Integer
Set Source = ThisWorkbook.Worksheets("Sheet1")
Set Target = ThisWorkbook.Worksheets("Sheet2")
Name = Source.Range("A3")
Problem = Source.Range("I13")
Do Until IsEmpty(Target.Cells(4 + i, 6)) ' This will loop down through non empty cells from row 5 of column 2
If Target.Cells(4 + i, 6) = Name Then
ItsAMatch = True
Target.Cells(4 + i, 7) = Problem ' This will overwrite your "Problem" value if the name was already in the column
Exit Do
End If
i = i + 1
Loop
' This will write new records if the name hasn't been already found
If ItsAMatch = False Then
Target.Cells(3, 6).End(xlDown).Offset(1, 0) = Name
Target.Cells(4, 6).End(xlDown).Offset(0, 1) = Problem
End If
Set Source = Nothing
Set Target = Nothing
End Sub
Thats the macro i have. As u said, i change the
othermacro
to CommandButton1_Click()
but it doesn't work
Quoting geoB except for one thing: when you .Show your UserForm from a main Sub, you can also .Hide it at the end and the macro that called it will continue its procedures.
Sub Okay_Click()
Dim sID1 As String, sID2 As String
' A little variation
If Me.TextBox1 = "" Or Me.TextBox2 = "" Then
MsgBox "Please fill all the input fields"
Exit Sub
End If
Me.Hide
End Sub
To address your TextBox, you can write in your main Sub UserForm3.TextBox1 for example
There is no need for an Enterval function. Instead, assume the user can read and follow instructions, then test whether that indeed is the case. Note that in your code ID and ID2 will never be used because they exist only within the scope of the subroutines in which they are declared and receive values.
To get started:
Sub Okay_Click()
Dim sID1 As String, sID2 As String
sID1 = UserForm3.TextBox1.Value
sID2 = UserForm3.TextBox2.Value
If Len(sID1 & vbNullString) = 0 Then
MsgBox "Box A is empty"
Exit Sub
End If
If Len(sID2 & vbNullString) = 0 Then
MsgBox "Box B is empty"
Exit Sub
End If
'Now do something with sID1, sID2
otherMacro(sID1, sID2)
End Sub
For your other macro, declare it like this:
Sub otherMacro(ID1, ID2)
...
End Sub
Also, the SetFocus method should occur in the form open event.
I have read and applied solution I found on similar topics but nothing seem to work in my case.
So, I want to pass a variable from one sub of my Module1 to a userform. It's a string called "provinceSugg".
Here is the relevant part of my code :
Public provinceSugg As String
Sub probaCity()
[...]
If province = "" And city <> "" Then
provinceSugg = sCurrent.Cells(p, db_column).Offset(0, 1).Value
UserForm2.Label1 = "Do you mean " & city & " in " & provinceSugg & " ?"
UserForm2.Label1.TextAlign = fmTextAlignCenter
UserForm2.Show
Else
End If
End Sub
And then in my userform code :
Private Sub userformBtn1_Click()
MsgBox provinceSugg
sMain.Range("J6").Value = provinceSugg
End Sub
When I run my program :
1/ I have the content of provinceSugg showing in the MsgBox called from my sub (so there is a provinceSugg, it's not an empty variable).
2/ The MsgBox called from the userform is empty (so passing the value failed) and my program crashes when running " sMain.Range("J6").Value = provinceSugg" with something like "Error 424 Object Required" (so the variable failed to pass to the userform).
I tried all the stuff I found on forum and here (different ways to indicate that provinceSugg is a public variable but still crashing...).
Thanks in advance for your help !
You would be able to create public variables within the Userform that can be set by the Module.
These variables are only accessible within the Userform as it is loaded.
Within the Userform, declare public variables for both objects.
Public sMain As Worksheet
Public provinceSugg as string
Private Sub userformBtn1_Click()
MsgBox provinceSugg
sMain.Range("J6").Value = provinceSugg
End Sub
Within the Module, you can assess both of those variables.
Sub probaCity()
[...]
If province = "" And city <> "" Then
provinceSugg = sCurrent.Cells(p, db_column).Offset(0, 1).Value
With UserForm2
.provinceSugg = provinceSugg
Set .sMain = sMain
.Label1 = "Do you mean " & city & " in " & provinceSugg & " ?"
.Label1.TextAlign = fmTextAlignCenter
.Show
End With
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim selectColumn
selectColumn= Split(Target.Address(1, 0), "$")(0)
Call UserFormStart(selectColumn)
End Sub
Inside Main Module
Public columnSelection As String
...
Public Sub UserFormStart(ByVal columnRef As String)
'MsgBox "Debug columnRef=" & columnRef
columnSelection = columnRef
UserForm1.Show
End Sub
Inside UserForm
Private Sub UserForm_Initialize()
'MsgBox "Debug UserForm_Initialize =" & columnSelection
...
End Sub
Worksheet_SelectionChange calls a sub on the module where columnSelection is declared as public and visable from the UserForm.
I used three different variables for the Column Reference to show that there is where the UserForm has access to the Module.
The above all works and took ages to find and work out hence the submission. Happy hunting folks
If you have a hidden worksheet in your workbook, simply write the parameter to be passed to the User Form somewhere on the worksheet and go read it from there in the User Form.
I have a form with a number of text boxes for user input (this is in a User Form not on the spreadsheet). I have a few boxes that are related to currency and I need them to show the comma and decimal point as the user enters their criteria into the box. So far I have found a bunch of the same formulas online but when I input my number into the box it goes with 4.00 (if i hit 4 first) and all i can change after that is the second 0. Here is something similar I see online:
textbox1 = format(textbox1, "$#,##0.00")
Also seen some with cDbl
No matter what I try it won't let me enter anything more than the first number I enter. I need help. Thanks!
Formatting as the user types in data gets very tricky. May be better to format after the entry is complete.
Entry can also be validated and old value restored if entry deemed invalid
Dim TextBox1oldValue As String
Private Sub TextBox1_AfterUpdate()
If IsNumeric(TextBox1) Then
TextBox1 = Format(TextBox1, "$#,##0.00")
Else
TextBox1 = TextBox1oldValue
End If
End Sub
Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
If IsNumeric(TextBox1) Then
TextBox1oldValue = Format(TextBox1, "$#,##0.00")
End If
End Sub
Private Sub UserForm_Initialize()
TextBox1oldValue = "$0.00"
TextBox1 = "$0.00"
End Sub
You need to use the TextBox Change event, like:
Private Sub TextBox1_Change()
If TextBox1 = vbNullString Then Exit Sub
If IsNumeric(TextBox1) Then CurrencyTransform(TextBox1)
End Sub
You then create the CurrencyTransform function to modify what it shows in the TextBox.
Try simply this...
Private sub textbox1_AfterUpdate()
textbox1 = format(textbox1, "$#,##0.00")
end sub
I wrote this inspired by chris' solution. It works while user is typing!
Private waiting As Boolean
Private Sub TextBox1_Change()
If waiting Then Exit Sub
waiting = True
TextBox1 = formatAsCurrency(TextBox1)
waiting = False
End Sub
Private Function formatAsCurrency(v As String)
If v = "" Then
formatAsCurrency = Format(0, "0.00")
Else
Dim vv As Variant
vv = Replace(v, ",", "")
vv = Replace(vv, ".", "")
formatAsCurrency = Format(vv / 100, "#,##0.00")
End If
End Function
Try this:
Private Sub TextBox1_Change()
TextBox1.Value = Format(TextBox1.Value, "$#,##0.00")
End Sub
This worked for me just fine, so it should help you as well.
If you want to do calculations that involve multiple text boxes, don't use .value after the name of the text box. Instead, use val( before the name of the text box while following it with an end parenthesis. I used .value and got weird results. Instead of, for example, $100 for TextBox1.Value + TextBox2.Value where TextBox1.Valueis equal to $25 and TextBox2.Value is equal to $75, I would get "$25$75".