I've a dashboard that uses a userform box field with checkboxes to choose what data to display on a chart.
My code is very copy and pasted.
Private Sub CommandButton21_Click()
UserForm1.Show
If Worksheets("Data Directorate").Range("X4").Value = True Then UserForm1.CheckBox1 = True
If Worksheets("Data Directorate").Range("X5").Value = True Then UserForm1.CheckBox2 = True
If Worksheets("Data Directorate").Range("X6").Value = True Then UserForm1.CheckBox3 = True
Is there a way to use a loop to do this?
I've more repeated code later on:
Private Sub CheckBox1_Click()
Select Case CheckBox1.Value
Case True
Worksheets("Data Directorate").Range("X4").Value = True
Case False
Worksheets("Data Directorate").Range("X4").Value = False
End Select
End Sub
This repeats for 24 check boxes. Would it be possible to loop this?
All great advice posted in this thread, so I'd like to add something that can maybe help to simplify your loops. Controls have a Tag property which, as best I can tell, does nothing other than to store additional information about the control.
Using this to our advantage, we can include the linked cell in the Tag property for each checkbox. (For example, enter X4 into the Tag for linkage to cell X4). This permits for less of the information to be hardcoded, which makes it more adaptable.
Finally, the code would look like this.
Private Sub UserForm_Click()
For Each octrl In Me.Controls
If TypeName(octrl) = "CheckBox" Then
Sheet1.Range(octrl.Tag).Value = octrl.Value
End If
Next octrl
End Sub
My approach in this scenario would be:
Set ControlSource property of the checkboxes to appropriate cells, leave only:
UserForm1.Show
One thing to simplify: instead of using If statements, just make the two sides equal. Like this:
Private Sub CommandButton21_Click()
UserForm1.Show
UserForm1.CheckBox1 = Worksheets("Data Directorate").Range("X4").Value
And the other one:
Private Sub CheckBox1_Click()
Worksheets("Data Directorate").Range("X4").Value = CheckBox1.Value
End Sub
I recommend using the change event instead of the click event. Some user might use Tab and Space, then the click event won't trigger. Like this:
Private Sub CheckBox1_Change()
...
About looping through the checkboxes in the CommandButton21_Click event, that depends on the order of your checkboxes and your table. This might work, but you will have to try it on your table (the order of the checkboxes compared to the order of your cells can ruin the game...)
Dim contr As control
dim i as integer
i=4
For Each contr In UserForm1.Controls
If TypeName(contr) = "CheckBox" Then
contr.Value = cells(i,24).Value 'column 24 is column X
i=i+1
End If
Next
A possible variation to work when CheckBoxes are not in the expected order or get added/removed:
Dim contr As control
Dim J as Integer
Dim Offset as Integer
Offset = 3 'set this to the difference between 1 and the first row containing data
For Each contr In UserForm1.Controls
If TypeName(contr) = "CheckBox" Then
'set j to the checkboxes "number"
J = cInt(right(contr.name, len(contr.name) - len("CheckBox")))
'use the checkbox number + offset to find the row we want
contr.Value = cells(J + Offset,24).Value 'column 24 is column X
End If
Next
Hope this helps.
For the second portion of your question:
Private Sub CheckBox1_Click()
Worksheets("Data Directorate").Range("X4").Value = CheckBox1.Value
End Sub
You can group the checkboxes inside a given frame and try the the following
Sub Test()
Dim i As Long
i = 5
For Each cb In UserForm1.Frame1.Controls
If Worksheets("Data Directorate").Range("X" & i).Value = True Then cb.Value = True
i = i + 1
Next cb
End Sub
Related
I need the help with the selection of multiple checkboxes by user. I have a userform in vba that has 8 checkboxes. I want user to select only ONE checkbox at a time. What is the shortest coding possible?
Please, try the next way:
Create a Sub able to untick all the check boxes, except the active one:
Public Sub UnTickCheckBoxes() 'Public to be also called from outside if use a class for event allocation
Dim i As Long
For i = 0 To Me.Controls.count - 1
If TypeName(Me.Controls(i)) = "CheckBox" And Not ActiveControl.Name = Me.Controls(i).Name Then
Application.EnableEvents = False
Me.Controls(i).value = False
Application.EnableEvents = True
End If
Next i
End Sub
Call the above sub from the Click event of all check boxes. You can use Change event for doing something else. The call can be done in this way:
Private Sub CheckBox1_Click()
If ActiveControl.value = True Then
UnTickCheckBoxes
End If
End Sub
2 bis. Automatic event allocation to all check boxes:
2.1 Create a class named "chkBoxesClickEvent". Copy and paste in it the next code:
Option Explicit
Public WithEvents chkBEvent As MSForms.CheckBox
Private Sub chkBEvent_Click()
If chkBEvent.value = True Then
chkBEvent.Parent.UnTickCheckBoxes
End If
End Sub
2.2 Declare on top of the form (in the declarations area) the next variable:
Private chkB() As New chkBoxesClickEvent
2.3 Place the next code in the UserForm_Initialize event:
Private Sub UserForm_Initialize()
Dim C As MSForms.Control, k As Long
ReDim chkB(Me.Controls.count): k = 1
For Each C In Me.Controls
If TypeOf C Is MSForms.CheckBox Then
Set chkB(k).chkBEvent = C: k = k + 1
End If
Next
ReDim Preserve chkB(k - 1)
End Sub
Do not forget to clear all check boxes Click event, if any code inside...
Show the form a see how it works, then send some feedback.
So I'm trying to use three Comboboxes to have a selection list for data input. I'm needing to make a selection in this order: Region -> Site -> Maintenance Plant. When a selection is made in the Region Combobox, then the Site Combobox list should filter to the options that pertain to the corresponding Region selection. Im thinking either a pivot table or vLookup needs to be used but I'm at a loss and have no clue how to get this done. Please help and thank you very much in advance.
Private Sub UserForm_Initialize()
Dim CreateBy As Range
Dim Region As Range
Dim Site As Range
Dim MaintPlant As Range
Dim Dept As Range
Dim Act As Range
Dim ImpActTyp As Range
Dim ValCat As Range
Dim ws As Worksheet
Set ws = Worksheets("LookupLists")
For Each CreateBy In ws.Range("RosterList")
With Me.CboCreateBy
.AddItem CreateBy.Value
End With
Next CreateBy
For Each Region In ws.Range("RegionList")
With Me.CboRegion
.AddItem Region.Value
End With
Next Region
For Each Site In ws.Range("SiteList")
With Me.CboSite
.AddItem Site.Value
End With
Next Site
For Each MaintPlant In ws.Range("MaintPlantList")
With Me.CboMntPlant
.AddItem MaintPlant.Value
End With
Next MaintPlant
For Each Dept In ws.Range("DeptList")
With Me.CboDept
.AddItem Dept.Value
End With
Next Dept
For Each Act In ws.Range("ActList")
With Me.CboAct
.AddItem Act.Value
End With
Next Act
For Each ImpActTyp In ws.Range("ImpActTypList")
With Me.CboImpActTyp
.AddItem ImpActTyp.Value
End With
Next ImpActTyp
For Each ValCat In ws.Range("ValCatList")
With Me.CboValCat
.AddItem ValCat.Value
End With
Next ValCat
Me.DateTextBox.Value = Format(Date, "Medium Date")
Me.PLife.Value = 0
Me.CSE.Value = 0
Me.CboRegion.SetFocus
End Sub
Get ready, because I'm about to reimagine your entire code here. I strongly recommend you create a backup of your original code module or workbook just due to the vast differences and if our ideas didn't align properly.
This will perform real-time filtering on your table, so keep this in mind using this method.
I did perform some testing on the following code, but I am human and threw this together in 20 mins or so. I wouldn't implement this in a real setting until you have fully tested the code and are comfortable with it.
And I just wanted to thank you for your use of Named Ranges. This made coding this easier.
You must enable the Microsoft Scripting Runtime library. This is used to grab the unique values from your tables. (Tools > References)
So to get things started, here is the entire code for your userform's code module:
Option Explicit
Private ws As Worksheet
Private tblLO As ListObject
Private Sub combo_region_Change()
Application.EnableEvents = False
Me.combo_maintPlant.Clear
Me.combo_site.Clear
'This is the first filter, so no worries about clearing entire AutoFilter
tblLO.AutoFilter.ShowAllData
Select Case Me.combo_region.Value
Case ""
Me.combo_site.Value = ""
Me.combo_maintPlant.Value = ""
Me.combo_site.Enabled = False
Me.combo_maintPlant.Enabled = False
Case Else
'If data is entered into first combobox, filter the table
tblLO.Range.AutoFilter 1, Me.combo_region.Value
'Populate the site combo box with new data
populateSiteCombo
'Enable the Site Combobox for user input
Me.combo_site.Enabled = True
End Select
Application.EnableEvents = True
End Sub
Private Sub combo_site_Change()
Application.EnableEvents = False
Me.combo_maintPlant.Clear
'Clear the filtering, then readd the Region's filter
tblLO.AutoFilter.ShowAllData
tblLO.Range.AutoFilter 1, Me.combo_region
Select Case Me.combo_site.Value
Case ""
Me.combo_maintPlant.Value = ""
Me.combo_maintPlant.Enabled = False
Case Else
'If data is entered into first combobox, filter the table
tblLO.Range.AutoFilter 2, Me.combo_site.Value
'Populate the Plant combo box with new data
populatePlantCombo
'Enable the Plant Combobox for user input
Me.combo_maintPlant.Enabled = True
End Select
Application.EnableEvents = True
End Sub
Private Sub populatePlantCombo()
'Grab unique values from Region column using Dictionary
Dim i As Long, regionDict As New Scripting.Dictionary
Dim arrReg() As Variant
'If it filters only 1 item, then it's just a single cell and not an arr
With ws.Range("MaintPlantList").SpecialCells(xlCellTypeVisible)
If .Count = 1 Then
Me.combo_maintPlant.AddItem .Value
Exit Sub
Else
arrReg = .Value
End If
End With
With New Scripting.Dictionary
For i = 1 To UBound(arrReg)
If Not .Exists(arrReg(i, 1)) Then
.Add arrReg(i, 1), "" 'We only add to dictionary for tracking
Me.combo_maintPlant.AddItem arrReg(i, 1)
End If
Next
End With
End Sub
Private Sub populateSiteCombo()
'Grab unique values from Region column using Dictionary
Dim i As Long, regionDict As New Scripting.Dictionary
Dim arrReg() As Variant
'If it filters only 1 item, then it's just a single cell and not an arr
With ws.Range("SiteList").SpecialCells(xlCellTypeVisible)
If .Count = 1 Then
Me.combo_site.AddItem .Value
Exit Sub
Else
arrReg = .Value
End If
End With
With New Scripting.Dictionary
For i = 1 To UBound(arrReg)
If Not .Exists(arrReg(i, 1)) Then
.Add arrReg(i, 1), "" 'We only add to dictionary for tracking
Me.combo_site.AddItem arrReg(i, 1)
End If
Next
End With
End Sub
Private Sub populateRegionCombo()
'Grab unique values from Region column using Dictionary
Dim i As Long, regionDict As New Scripting.Dictionary
Dim arrReg() As Variant
arrReg = ws.Range("RegionList").Value
With New Scripting.Dictionary
For i = 1 To UBound(arrReg)
If Not .Exists(arrReg(i, 1)) Then
.Add arrReg(i, 1), "" 'We only add to dictionary for tracking
Me.combo_region.AddItem arrReg(i, 1)
End If
Next
End With
End Sub
Private Sub UserForm_Initialize()
Set ws = ThisWorkbook.Worksheets("LookupLists") 'Module-defined var
Set tblLO = ws.ListObjects("Table1") 'Module-defined var
tblLO.AutoFilter.ShowAllData
Me.combo_maintPlant.Enabled = False
Me.combo_site.Enabled = False
'We only populate this one during init because the others
'will populate once a value is used in this box
populateRegionCombo
End Sub
If you decided to scroll down to understand what's going on here, then great.
Let's start with the initialization:
Private Sub UserForm_Initialize()
Set ws = ThisWorkbook.Worksheets("LookupLists") 'Module-defined var
Set tblLO = ws.ListObjects("Table1") 'Module-defined var
tblLO.AutoFilter.ShowAllData
Me.combo_maintPlant.Enabled = False
Me.combo_site.Enabled = False
'We only populate this one during init because the others
'will populate once a value is used in this box
populateRegionCombo
End Sub
We defined the module variables ws and tblLO. I'm not a huge fan of module-scoped variables, but we can usually get along when they are private vars to a userform module. Now the other functions in the code module can access these.
We reset autofiltering and disabled the two combo boxes that shouldn't be used until a selection is made for the region. Only after the region is selected will the next box be available for selection. We will handle these using Change Events for the comboboxes.
The userform is mostly controlled by the combo_region_change and combo_site_change events. Everytime region_change is fired, it will clear all the other combo boxes to redetermine it's new value. Then it will refilter as appropriately. The combo_site does the same, but it only clears the maintaince box. These event handlers also establish which of the other combox boxes are enabled depending on their values. So if you where to completely clear the site box for example, it will disable access to the Plant box again.
Finally you just have the "populate subs". Their jobs are simply to (re)populate the next combo box once the appropriate event handler is triggered.
Tip: If you feel the need to reset the filtering once you close your userform, you can just place the code to reset it in a UserForm_Terminate() event. It makes no difference to the above code if autofilter is enabled or not prior to it running, so that is preference only.
I have this code but it only work for my first row.
It is suppose to look if the checkbox on B, C or D is checked, and if so, a date + username will automaticaly fill in F and G.
here is a picture of my table:
This is what my code looks like:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Range("B2") Or Range("C2") Or Range("D2") = True Then
Range("G2").Value = Environ("Username")
Range("F2").Value = Date
Else
Range("F2:G2").ClearContents
End If
End Sub
Enter this code in a regular module, select all your checkboxes and right-click >> assign macro then choose ReviewRows.
This will run the check whenever a checkbox is clicked - a bit of overhead since all rows will be checked, but should not be a big deal.
Sub ReviewRows()
Dim n As Long
For n = 1 To 100 'for example
With Sheet1.Rows(n)
If Application.CountIf(.Cells(2).Resize(1, 3), "TRUE") > 0 Then
If Len(.Cells(6).Value) = 0 Then 'only enter if currently empty?
.Cells(6) = Date
.Cells(7) = Environ("Username")
End If
Else
.Cells(6).Resize(1, 2).ClearContents
End If
End With
Next n
End Sub
If you want to be more precise then Application.Caller will give you the name of the checkbox which was clicked, and you can use that to find the appropriate row to check via the linkedCell.
Sub ReviewRows()
Dim n As Long, shp As CheckBox, c As Range, ws As Worksheet
Set ws = ActiveSheet
On Error Resume Next 'ignore error in case calling object is not a checkbox
Set shp = ActiveSheet.CheckBoxes(Application.Caller) 'get the clicked checkbox
On Error GoTo 0 'stop ignoring errors
If Not shp Is Nothing Then 'got a checkbox ?
If shp.LinkedCell <> "" Then 'does it have a linked cell ?
With ws.Range(shp.LinkedCell).EntireRow
If Application.CountIf(.Cells(2).Resize(1, 3), "TRUE") > 0 Then
If Len(.Cells(6).Value) = 0 Then 'only enter if currently empty?
.Cells(6) = Date
.Cells(7) = Environ("Username")
End If
Else
.Cells(6).Resize(1, 2).ClearContents
End If
End With
End If 'has linked cell
End If 'was a checkbox
End Sub
However this appraoch is sensitive to the exact positioning of your checkbox
You have a long way to go!
Unfortunately, If Range("B2") Or Range("C2") Or Range("D2") = True Then is beyond repair. In fact, your entire concept is.
Start with the concept: Technically speaking, checkboxes aren't on the worksheet. They are on a layer that is superimposed over the worksheet. They don't cause a worksheet event, nor are they responding to worksheet events. The good thing is that they have their own.
If Range("B2") Or Range("C2") Or Range("D2") = True Then conflates Range with Range.Value. One is an object (the cell), the other one of the object's properties. So, to insert sense into your syntax it would have to read, like, If Range("B2").Value = True Or Range("C2").Value = True Or Range("D2").Value = True Then. However this won't work because the trigger is wrong. The Worksheet_Change event won't fire when when a checkbox changes a cell's value, and the SelectionChange event is far too common to let it run indiscriminately in the hope of sometimes being right (like the broken clock that shows the correct time twice a day).
The answer, therefore is to capture the checkbox's click event.
Private Sub CheckBox1_Click()
If CheckBox1.Value = vbTrue Then
MsgBox "Clicked"
End If
End Sub
Whatever you want to do when the checkbox is checked must be done where it now shows a MsgBox. You can also take action when it is being unchecked.
I am currently working on a userform to create an order for users at a company to send to my dept.
At the moment i have come to a standstill as i am struggling to work out the following.
I have a combobox which has a list of products our business offers. Based on the selection i want to be able to add labels and textbox which require the user to enter data for example.
If this selection in the combo box then
Enter name, date required, location of user etc.
Also this needs to be specific to the combobox selection.
Any help would be much appreciated :)
UPDATE
Apologies, as i did not have any code for that function I did not add any Here is the code i have.
Private Sub CommandButton1_Click()
Windows("RFS User Form Mock.xlsm").Visible = True
End Sub
Private Sub LegendDefinition_Change()
LegendDefinition.Locked = True
End Sub
Private Sub RequestList_Change()
Dim i As Long, LastRow As Long
LastRow = Sheets("Definition").Range("A" & Rows.Count).End(xlUp).Row
For i = 2 To LastRow
If Sheets("Definition").Cells(i, "A").Value = (Me.RequestList) Then
Me.DefinitionBox = Sheets("Definition").Cells(i, "B").Value
End If
Next
End Sub
Private Sub RequestList_DropButtonClick()
Dim i As Long, LastRow As Long
LastRow = Sheets("Definition").Range("A" & Rows.Count).End(xlUp).Row
If Me.RequestList.ListCount = 0 Then
For i = 2 To LastRow
Me.RequestList.AddItem Sheets("Definition").Cells(i, "A").Value
Next i
End If
End Sub
Sub UserForm_Initialize()
SiteList.List = Array("Birmingham", "Bristol", "Cardiff", "Chelmsford", "Edinburgh", "Fenchurch Street", "Glasgow", "Guernsey", "Halifax", "Homeworker", "Horsham", "Ipswich", "Jersey", "Leeds", "Leicester", "Lennox Wood", "Liverpool", "Manchester", "Peterborough", "Redhill", "Sunderland", "Madrid")
End Sub
Private Sub VLookUp_Change()
VLookUp.Locked = True
End Sub
When posting a question, you are expected to provide some code showing where you're standing trying to address the problem. Here's nevertheless a short demo that will give you a starting point.
Create a new UserForm and put a combobox, a label and a textbox on it; ensure they're named ComboBox1, Label1 and TextBox1, respectively.
Then, paste this code in the form's module:
Option Explicit
Private Sub ComboBox1_Change()
Dim bVisible As Boolean
'Only show the label and the textbox when the combo list index is 1, which corresponds to "Item 2".
'Note: bVisible = (ComboBox1.Text = "Item 2") would also work.
bVisible = (ComboBox1.ListIndex = 1)
Label1.Visible = bVisible
TextBox1.Visible = bVisible
End Sub
Private Sub UserForm_Layout()
'Populate the combo.
ComboBox1.AddItem "Item 1", 0
ComboBox1.AddItem "Item 2", 1
'Note: the code below could be removed by setting the corresponding
'design-time properties from the form designer.
ComboBox1.Style = fmStyleDropDownList
Label1.Visible = False
TextBox1.Visible = False
End Sub
Then press F5 to show the form. You'll notice that the label and textbox are only visible when the combo shows "Item 2". The visibility adjustment is performed within the ComboBox1_Change event handler.
If you plan on having numerous controls shown / hidden depending on your combo's value, you could group them into one or more Frame controls, and show / hide those frames instead.
I want to write a value (1) into a desired cell within Excel 2007, when I select a checkbox. The checkbox is in a Visual Basic userform, not on the active sheet itself.
The value (1) must revert back to zero when the checkbox is not selected.
I managed to get it working, however, I have more than 300 check-boxes, and want to know how to create one code that will do it in a loop?
{
Private Sub CheckBox1_Click()
If CheckBox1.Value = True Then
ThisWorkbook.Sheets("sheet3").Range("b8").Value = 1
Else: ThisWorkbook.Sheets("sheet3").Range("b8").Value = 0
End If
End Sub
Private Sub CheckBox2_Click()
If CheckBox2.Value = True Then
ThisWorkbook.Sheets("sheet3").Range("b9").Value = 1
Else: ThisWorkbook.Sheets("sheet3").Range("b9").Value = 0
End If
End Sub
Private Sub CheckBox3_Click()
If CheckBox3.Value = True Then
ThisWorkbook.Sheets("sheet3").Range("b10").Value = 1
Else: ThisWorkbook.Sheets("sheet3").Range("b10").Value = 0
End If
End Sub
}
from checkbox 1 to checkbox 300, the cell range will be "B8" all the
way to "B308"
checkbox1 = cell range b8
checkbox2 = cell range b9
checkbox3 = cell range b10
checkbox4 = cell range b11
etc.......
Use IIf to set a value based on True or False.
Private Sub CheckBox1_Click()
ThisWorkbook.Sheets("sheet3").Range("B8").Value = IIf(CheckBox1.Value, 1, 0)
End Sub
Private Sub CheckBox2_Click()
ThisWorkbook.Sheets("sheet3").Range("B9").Value = IIf(CheckBox2.Value, 1, 0)
End Sub
Private Sub CheckBox3_Click()
ThisWorkbook.Sheets("sheet3").Range("B10").Value = IIf(CheckBox3.Value, 1, 0)
End Sub
I don't know how your workbook/userform looks in detail, but this should show pretty good how to do it:
First, we need the class which we will call Class1. In this class we put the code:
Option Explicit
Public WithEvents CBoxC As MSForms.CheckBox
Private Sub CBoxC_Change()
Dim i As Long
i = CLng(Replace(CBoxC.Name, "CheckBox", ""))
Sheet3.Cells(i + 7, 2).Value = 0 - CBoxC.Value
End Sub
Now we need a variable which "transfere" the events to our class. We jast add to any module:
Option Explicit
Public CBox() As New Class1
As the last step, we need to insert all the controls into our variable. So we add (or just include, if already there):
Option Explicit
Private Sub UserForm_Initialize()
Dim b As Variant
For Each b In Me.Controls
If TypeName(b) = "CheckBox" Then
If (0 / 1) + (Not Not CBox) = 0 Then ReDim CBox(0) Else ReDim Preserve CBox(UBound(CBox) + 1)
Set CBox(UBound(CBox)).CBoxC = b
End If
Next
End Sub
Instead of _Click we better use _Change. This way also keyboard-input will work...
As it is pretty much no code, it also should be self explaining. Just keep in mind, that such events will come last. (which should not matter in your case)
If you still have any questions, just ask ;)