I am creating an excel file that is a checklist, currently i have 73 checkboxes in Column D, where in column E it will populate the name of the user based on the username in the options field.
Currently i Have code such as:
Sub CheckBox1_Click()
If ActiveSheet.CheckBoxes("Check Box 1").Value = 1 Then
Range("E3").Value = Application.UserName
Else: Range("E3").Value = ""
End If
End Sub
Sub CheckBox2_Click()
If ActiveSheet.CheckBoxes("Check Box 2").Value = 1 Then
Range("E4").Value = Application.UserName
Else: Range("E4").Value = ""
End If
End Sub
For each checkbox in column D. It does work but I need to now replicate Column D into Columns F,H,J,L for other days of the week and I am curious if there is a faster way to do this and a cleaner way to do this instead of having a long list.
Try something like this. You will have to format each checkbox and assign this macro to each of them, from the Format | Assign Macro option.
Sub Generic_ChkBox()
Dim cbName As String
Dim cbCell As Range
Dim printValue as String
cbName = Application.Caller
Set cbCell = ActiveSheet.CheckBoxes(cbName).TopLeftCell
Select Case cbCell.Column
Case 4
'prints the username in column E
printValue = Application.UserName
Case 6
'prints "Something else" in column G
printValue = "Something else"
Case 8
'prints "etc..." in column I, etc.
printValue = "etc..."
Case 10
printValue = "etc..."
Case 12
printValue = "etc..."
End Select
If ActiveSheet.CheckBoxes(cbName).Value = 1 Then
cbCell.Offset(0, 1).Value = printValue
Else
cbCell.Offset(0, 1).Value = vbNullString
End If
End Sub
I have assumed your are going to assign the username value to next cell of CheckBox.
For D4 is having checkbox then value will be E4.
Sub ProcessAllCheckBox()
Dim ws As Worksheet, s As Shape
Sheets("Sheet1").Columns("A:Z").ClearContents
Set ws = ActiveSheet
For Each chk In ActiveSheet.CheckBoxes
If chk.Value = 1 Then
Set s = ws.Shapes(chk.Caption)
Sheets("Sheet1").Range(Cells(s.TopLeftCell.Row, s.TopLeftCell.Column + 1), Cells(s.TopLeftCell.Row, s.TopLeftCell.Column + 1)).Value = Application.UserName
End If
Next
End Sub
Please update the below code in WorkShee Active
Private Sub Worksheet_Activate()
For Each chk In ActiveSheet.CheckBoxes
chk.OnAction = "ProcessAllCheckBox"
Next
ProcessAllCheckBox
End Sub
Related
I have this code but its hard coded. I need it to be automated in a loop or something else instead of a case statement till the empty column stops but more column will be added in a form. So the range can go from B2 to the late column in the excel sheet only if the column header is there and then it stops. It will shows the content in each column. Please note the excel sheet is called Area
Can this be done?
Private Sub ComboBox3_Change()
Dim i As Long
i = ComboBox3.ListIndex
ComboBox4.Clear
Select Case i
Case Is = 0
With Worksheets("Area")
ComboBox4.List = .Range("**B2:B**" & .Range("**b**" & Rows.Count).End(xlUp).Row).Value
End With
Case Is = 1
With Worksheets("Area")
ComboBox4.List = .Range("**C2:C**" & .Range("**c**" & Rows.Count).End(xlUp).Row).Value
End With
Case Is = 2
With Worksheets("Area")
ComboBox4.List = .Range("**D2:D**" & .Range("**d**" & Rows.Count).End(xlUp).Row).Value
End With
Case Is = 3
With Worksheets("Area")
ComboBox4.List = .Range("**E2:E**" & .Range("**e**" & Rows.Count).End(xlUp).Row).Value
End With
Case Is = 4
With Worksheets("Area")
ComboBox4.List = .Range("**F2:F**" & .Range("**f**" & Rows.Count).End(xlUp).Row).Value
End With
End Select
End Sub
Something like this should work for you:
Private Sub ComboBox3_Change()
Dim ws As Worksheet
Dim ColNum As Long
Set ws = ActiveWorkbook.Sheets("Area")
ColNum = Me.ComboBox3.ListIndex + 2
Me.ComboBox4.Clear
If ColNum < 2 Then Exit Sub 'Nothing selected
Me.ComboBox4.List = ws.Range(ws.Cells(2, ColNum), ws.Cells(ws.Rows.Count, ColNum).End(xlUp)).Value
End Sub
If you want to enumerate the column number into column name use Chr(65) where 65 is the ASCII value for A.
In your case add 66 to your integer i like Chr(66+i) which returns B
Also, there is a function to convert column number into column name in excel, =SUBSTITUTE(ADDRESS(1,col_number,4),"1","") although I haven't tried it in VBA.
In your specific case, you don't need a case statement at all
Private Sub ComboBox3_Change()
Dim i As Long
i = ComboBox3.ListIndex
ComboBox4.Clear
With Worksheets("Area")
ComboBox4.List = .Range("**E2:E**" & .Range("**e**" & Rows.Count).End(xlUp).Row).Offset(0,i).Value
End Sub
might need some tweaking since I can't test it out. But offset by 0 rows and i columns is the best solution for you
I'm creating a workbook, which tracks available rentals per month. It is divided into 12 sheets, one for each month. The first three columns of each sheet track the type of accommodation, number of bedrooms and what's included in the rental price. The concept there is that there will be a drop-down combo box that allows the user to fill in with a point-and-click option rather than typing things out in order to reduce input errors.
I set up a fixed array, the contents in which changes depending on what column that active cell is in, and then the array is assigned to the combo box. The code lives in the Sheet1 Module under the combo box code and the ThisWorkbook module calls it under SheetSelectionChange, so as to avoid repeating the code in each sheet.
A Standard Module makes the array public
All 12 combo boxes share the same name, cboOptions, and they populate correctly, regardless of what sheet is chosen. My problem is that none of the combo boxes return the listindex value of the choice that's made, regardless of the code telling it to do so. I've been testing to see the value of the position returned against the value of the position chosen, but I have not been able to establish a pattern. I thought about clearing the variables and arrays, thinking that might be what's messing with the code, but it seems to be having no effect. I've read what I could on the issue, but I'm out of ideas on what might be the problem...thank you in advance!
Code in Sheet1 module:
Private Sub cboOptions_Change()
Erase myarray()
cboOptions.Visible = True
cboOptions.Enabled = True
cboOptions.Clear
n = ActiveCell.Row
If n >= 3 And n < 10000 Then
If ActiveSheet.Range(ActiveCell.Address).Address = Range("A" & n).Address Then
myarray(1) = "Apartment"
myarray(2) = "Room"
myarray(3) = "Townhouse"
myarray(4) = "House"
ElseIf ActiveSheet.Range(ActiveCell.Address).Address = Range("B" & n).Address Then
myarray(1) = "1"
myarray(2) = "2"
myarray(3) = "3"
myarray(4) = "4"
myarray(5) = "5"
ElseIf ActiveSheet.Range(ActiveCell.Address).Address = Range("C" & n).Address Then
myarray(1) = "Heat & Water"
myarray(2) = "All-inclusive"
Else
cboOptions.Enabled = False
cboOptions.Visible = False
End If
End If
'ActiveSheet.cboOptions.ListIndex = 0
'Dim x As Long
'MsgBox ActiveSheet.Name
With ActiveSheet
.cboOptions.Left = .Range(ActiveCell.Address).Left
.cboOptions.Top = .Range(ActiveCell.Address).Top
.cboOptions.List = myarray()
With .cboOptions
'the problem is that x needs to get assigned a value from the combo box before it continues to execute
x = .List(.ListIndex)
'MsgBox x
End With
.Range(ActiveCell.Address) = x 'myarray(x)
.Columns(ActiveCell.Column).ColumnWidth = cboOptions.Width * 0.18
x = 0
Erase myarray()
End With
End Sub
Code in ThisWorkbook:
Sub Workbook_SheetSelectionChange(ByVal sh As Object, ByVal Target As Range)
Application.Run "Sheet1.cboOptions_Change"
End Sub
Code in Module1:
Option Explicit
Public myarray(0 To 5) As String
The nature of the problem seems to be that using more than one array for one combo box breaks down how the listindex values are calculated. I broke down the code to its component features to see if the issue persisted
1) Made a new file and put the code in Sheet1
2) Made separate fixed arrays for each kind of input
3) Created a separate routine for each kind of input
Using ON ERROR RESUME NEXT at the beginning of each routine overlooks the error and the code works properly. Alternatively, putting in a break where the integer variable is given the listindex value of the combo box allows the user to make a choice and assign a value to the integer variable, before continuing. Otherwise, its default value is -1 and returns an error; using .list(.listindex) did not make any difference, suggesting that the code needs to wait for user input (using a combobox event other than Change?).
May just need to establish a separate combo box for each column. Anyway, the code below is the sticks-and-stones version of the above, for a single sheet, and it will do the job if applied to each sheet module in the workbook:
Sub monthnames()
'add month names to the first cell of each sheet
Dim n As Integer
'Sheets(1).Activate
For n = 1 To 12
Sheets.Add After:=ActiveSheet
ThisWorkbook.Sheets(n).Cells(1, 1) = MonthName(n)
Next
End Sub
Private Sub cboOptions_Change()
Dim myarray(1 To 4) As String
Dim myarray2(1 To 5) As String
Dim myarray3(1 To 2) As String
cboOptions.Enabled = True
cboOptions.Visible = True
Dim n As Integer
n = ActiveCell.Row
If n >= 3 And n < 10000 Then
If Range(ActiveCell.Address).Address = Range("A" & n).Address Then
myarray(1) = "Apartment"
myarray(2) = "Room"
myarray(3) = "Townhouse"
myarray(4) = "House"
cboOptions.List = myarray()
inputdata myarray(), n
ElseIf Range(ActiveCell.Address).Address = Range("B" & n).Address Then
myarray2(1) = "1"
myarray2(2) = "2"
myarray2(3) = "3"
myarray2(4) = "4"
myarray2(5) = "5"
cboOptions.List = myarray2()
inputdata2 myarray2(), n
ElseIf Range(ActiveCell.Address).Address = Range("C" & n).Address Then
myarray3(1) = "Heat & Water"
myarray3(2) = "All-inclusive"
cboOptions.List = myarray3()
inputdata3 myarray3(), n
Else
cboOptions.Enabled = False
cboOptions.Visible = False
End If
End If
End Sub
Sub inputdata(myarray, n) 'myarray3, )
On Error Resume Next
Dim x As Integer
cboOptions.Left = Range(ActiveCell.Address).Left
cboOptions.Top = Range(ActiveCell.Address).Top
Columns(ActiveCell.Column).ColumnWidth = cboOptions.Width * 0.18
If Range(ActiveCell.Address).Address = Range("A" & n).Address Then
x = cboOptions.ListIndex + 1
Range(ActiveCell.Address) = myarray(x)
Else
Exit Sub
End If
End Sub
Sub inputdata2(myarray2, n)
On Error Resume Next
Dim y As Integer
cboOptions.Left = Range(ActiveCell.Address).Left
cboOptions.Top = Range(ActiveCell.Address).Top
Columns(ActiveCell.Column).ColumnWidth = cboOptions.Width * 0.18
If Range(ActiveCell.Address).Address = Range("B" & n).Address Then
y = cboOptions.ListIndex + 1
Range(ActiveCell.Address) = myarray2(y)
Else
Exit Sub
End If
End Sub
Sub inputdata3(myarray3, n)
On Error Resume Next
Dim z As Integer
cboOptions.Left = Range(ActiveCell.Address).Left
cboOptions.Top = Range(ActiveCell.Address).Top
Columns(ActiveCell.Column).ColumnWidth = cboOptions.Width * 0.18
If Range(ActiveCell.Address).Address = Range("C" & n).Address Then
z = cboOptions.ListIndex + 1
Range(ActiveCell.Address) = myarray3(z)
Else
Exit Sub
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Call cboOptions_Change
End Sub
Following code is for counting the number of checked checkboxes in a formfield. How can I modify it to count the same object in a normal table, and not a form field?
Private Sub CommandButton2_Click()
Dim i As Long, j As Long, k As Long
k = 0
With ActiveDocument
With .Tables(1)
j = 3
For i = 1 To .Rows.Count
If .Cell(i, j).Range.FormFields(1).CheckBox.Value = True Then
k = k + 1
End If
Next i
i = .Rows.Count
End With
End With
MsgBox k & " instances were found"
End Sub
use the ContentControls property of the Range object to return a ContentControls object and exploit it
here's some examples of how to count checkboxes or checked checkboxes in a table or a in a single column of it:
Option Explicit
Sub main()
With ActiveDocument
MsgBox CountCheckBoxes(.Tables(1)) & " CheckBox instances were found"
MsgBox CountCheckedCheckBoxes(.Tables(1)) & " checked CheckBox instances were found"
MsgBox CountCheckBoxesInColumn(.Tables(1), 1) & " CheckBox instances were found in column 1"
MsgBox CountCheckedCheckBoxesInColumn(.Tables(1), 1) & " checked CheckBox instances were found in column 1"
End With
End Sub
Private Function CountCheckBoxes(table As table, Optional col As Variant) As Long
Dim cc As ContentControl
With table
For Each cc In .Range.ContentControls
If cc.Type = wdContentControlCheckBox Then CountCheckBoxes = CountCheckBoxes + 1
Next cc
End With
End Function
Private Function CountCheckedCheckBoxes(table As table) As Long
Dim cc As ContentControl
With table
For Each cc In .Range.ContentControls
If cc.Type = wdContentControlCheckBox Then If cc.Checked Then CountCheckedCheckBoxes = CountCheckedCheckBoxes + 1
Next cc
End With
End Function
Private Function CountCheckBoxesInColumn(table As table, col As Long) As Long
Dim i As Long
With table
For i = 1 To .Rows.count
CountCheckBoxesInColumn = CountCheckBoxesInColumn + .Cell(i, col).Range.ContentControls.count
Next i
End With
End Function
Private Function CountCheckedCheckBoxesInColumn(table As table, col As Long) As Long
Dim i As Long
With table
For i = 1 To .Rows.count
CountCheckedCheckBoxesInColumn = CountCheckedCheckBoxesInColumn + CountCheckBoxesCheked(.Cell(i, col).Range)
Next i
End With
End Function
Function CountCheckBoxesCheked(rng As Range) As Long
Dim cc As ContentControl
With rng
For Each cc In .ContentControls
If cc.Type = wdContentControlCheckBox Then If cc.Checked Then CountCheckBoxesCheked = CountCheckBoxesCheked + 1
Next cc
End With
End Function
Hope you have an elegant solution for what is probably a simple problem!
I am using ActiveX option buttons, but within a worksheet and not a userform or a group box because of the way the sheet was designed. The code is contained as a sub within an option button code form.
This code is pretty self-explanatory of what I'm trying to do:
Public Sub SectionD_Click()
If OptionButton1.Value = True Then
ThisWorkbook.Sheets("Boolean").Range("B2").Value = 1
ElseIf OptionButton2.Value = True Then
ThisWorkbook.Sheets("Boolean").Range("B2").Value = 0
End If
If OptionButton3.Value = True Then
ThisWorkbook.Sheets("Boolean").Range("B3").Value = 1
ElseIf OptionButton4.Value = True Then
ThisWorkbook.Sheets("Boolean").Range("B3").Value = 0
End If
If OptionButton5.Value = True Then
ThisWorkbook.Sheets("Boolean").Range("B4").Value = 1
ElseIf OptionButton6.Value = True Then
ThisWorkbook.Sheets("Boolean").Range("B4").Value = 0
End If
End Sub
I would like to make it such that the number following "OptionButton" changes values using a simple 'i = i + 2' type statement but it seems some VBA variable/expression/object limitations will not let me (sorry I'm a noob here, not sure what the proper terminology should be).
Would really appreciate if anyone could point me in the right direction here! I have to look through 25 or so option button pairs, and I would very much like the code to just be 5 simple lines rather than a hundred over lines doing the same thing!
I can name that tune with one line of code!!
Public Sub SectionD_Click(): Dim i As Integer: Dim rw As Long: rw = 2: With Worksheets("Sheet1"): For i = 1 To 10 Step 2: If .OLEObjects("OptionButton" & i).Object.Value Then: Worksheets("Boolean").Cells(rw, "B").Value = 0: ElseIf .OLEObjects("OptionButton" & i).Object.Value Then: Worksheets("Boolean").Cells(rw, "B").Value = 0: End If: rw = rw + 1: Next: End With:End Sub:
But I think that 16 lines is prettier.
Public Sub SectionD_Click()
Dim i As Integer
Dim rw As Long
rw = 2
With Worksheets("Sheet1")
For i = 1 To 10 Step 2
If .OLEObjects("OptionButton" & i).Object.Value Then
Worksheets("Boolean").Cells(rw, "B").Value = 0
ElseIf .OLEObjects("OptionButton" & i).Object.Value Then
Worksheets("Boolean").Cells(rw, "B").Value = 0
End If
rw = rw + 1
Next
End With
End Sub
5 lines? Really? :)
That's the best I can do:
Option Explicit
Public Sub SectionD_Click()
With ThisWorkbook.Sheets("Boolean")
Call CheckValue(.OptionButton1, .OptionButton2, .Range("B2"))
Call CheckValue(.OptionButton3, .OptionButton4, .Range("B3"))
End With
End Sub
Sub CheckValue(btn1 As Object, btn2 As Object, my_cell As Range)
If btn1.Value Then
my_cell.Value = 1
ElseIf btn2.Value Then
my_cell = 0
End If
End Sub
I have a working macro right now that automatically generates a list of all the work sheets I have in my file:
Sub RefreshStocks()
Dim x As Integer
Dim count As Integer
For x = 4 To Worksheets.count
Select Case Worksheets(x).Name
Case "Calculator", "Index Composition", "Market Value", "Watch List", "REF DATA"
'Do Nothing
Case Else
count = count + 1
Cells(count, 2).Value = Worksheets(x).Name
End Select
Next x
End Sub
The macro is working fine, but I want to know what I should do if I have multiple worksheets that I want to exclude from the list. For example, if want to exclude the worksheets named "Reference data" and "Process Guide".
What I'm trying to do is to add an IF statement that has the sub ignore the worksheets named "Reference data" or "Process Guide" from the generated list.
Sub WSNames()
Dim x As Integer
For x = 4 To Worksheets.Count
If Worksheet.Name = "Reference data" Or "Process Guide" Then
'IGNORE this Worksheet and should not be included
Else
Cells(x, 2).Value = Worksheets(x).Name
End If
Next x
End Sub
Could someone help out to correct the code above.
Select case is ideal for this situation.
Sub WSNames()
Dim x As Integer
Dim count as Integer
count = 4
For x = 1 To Worksheets.Count
Select Case Worksheets(x).Name
Case "Reference data", "Process Guide"
'Do Nothing
Case Else
Cells(count, 2).Value = Worksheets(x).Name
count = count + 1
End Select
Next x
End Sub
Thanks YowE3K!
Sub WSNames()
Dim x As Integer
Dim r As Integer
r = 3
For x = 4 To Worksheets.Count
IF Worksheets(x).name = "Reference data" or _
Worksheets(x).name = "Process Guide" then
'IGNORE this Worksheet and should not be included
Else
r = r + 1
Cells(r, 2).Value = Worksheets(x).Name
End If
Next x
End Sub
I included a new variable, r, so that there wouldn't be gaps in your list where the excluded worksheet names would have otherwise been.
just use if worksheet(x).name<>"ref data" and worksheet(x).name<>"Proc guide" then
Sub WSNames_another()
Dim sh As Worksheet
For Each sh In ActiveWorkbook.Sheets
If sh.Name = "Reference data" Or sh.Name = "Process Guide" Then
'IGNORE this Worksheet and should not be included
Else
'Do what every you want
End If
Next
End Sub
OR
Sub WSNames()
Dim x As Integer
For x = 4 To Worksheets.Count
If Worksheets(x).Name = "Reference data" Or Worksheets(x).Name = "Process Guide" Then
'IGNORE this Worksheet and should not be included
Else
Cells(x, 2).Value = Worksheets(x).Name
End If
Next x
End Sub