Using relative references to reuse a macro for a control - vba

I'm trying to write an Excel 2007 macro for a coworker, but my VBA skills are pretty basic (pardon the pun). Essentially, what needs to happen is, when a checkbox is clicked, the neighboring cell to the right is filled with the username of the person logged in.
So far, here's the code I've come up with that allows me to do that:
Sub CheckBox1_Click()
Range("J4").Activate
If ActiveCell.Offset(0, 18).Value = True Then
ActiveCell.Offset(0, 1).Value = Environ("UserName")
Else
ActiveCell.Offset(0, 1).Clear
End If
End Sub
Just for the sake of reference, that "ActiveCell.Offset(0,18)" refers to a cell that is linked to the checkbox in question and contains its true/false value.
(EDIT: Also, the reason cell J4 is activated is because in this case, it's the cell containing the ActiveX checkbox)
That works perfectly, but that's not my problem. My problem is this: there are 49 more checkboxes in that row, and three more rows on this sheet, and 45 more sheets in this book. I do NOT want to have to copy paste the same code into a unique macro just to change the active cell. More importantly, as a good programmer, I shouldn't be repeating code like that. How should I write this so that I don't have to refer to a distinct cell every time?
EDIT 2: Holy smokes, Lance just helped me realize I was mistaken. The sheet uses form controls, not ActiveX controls. Greatly sorry, everyone.

While this is easy to do with a Sheet object, it's pretty hard to do with an ActiveX Control object. You can't self-reference the name of an ActiveX Control in its event, unless it's passed to it, and you also can't reference the name of the event subroutine to extract the name, and you can't reference the name of the routine that called a routine.
I also attempted to trigger off of the Worksheet Change and SelectionChange events, but those don't trigger off of a checkbox change, even if it has a LinkedCell that changes
What I finally came up with was the somewhat generic wrapper for the click event, that you'll have to modify the string to match the Checkbox name:
Private Sub CheckBox1_Click()
NameCopy Me, "CheckBox1"
End Sub
and then a Namecopy function that sets the cell -7 to the left of the LinkedCell to the name value.
Public Sub NameCopy(wsheet As Worksheet, cname As String)
If wsheet.OLEObjects(cname).Object.Value = True Then
Range(wsheet.OLEObjects(cname).LinkedCell).Offset(0, -7).Value = Environ("UserName")
End If
End Sub
It's easier with a Forms checkbox, you can use this Macro for all your checkboxes. Just remember to set the Macro to this:
Public Sub NameCopy()
Dim shp As Shape
Set shp = ActiveSheet.Shapes(Application.Caller)
If shp.ControlFormat.Value = xlOn Then
ActiveSheet.Range(shp.ControlFormat.LinkedCell).Offset(0, -7).Value = Environ("UserName")
End If
End Sub

Since you are using form controls, this is really easy. You can use Application.Caller to have the code access the clicked checkbox, and then use it's TopLeftCell property to get where the checkbox is located, and then you can perform whatever operation you want. In your case, something like this I'm guessing:
Sub Checkbox_Click()
With ActiveSheet.CheckBoxes(Application.Caller)
If .Value = 1 Then 'Checkbox is checked
.TopLeftCell.Offset(, 1).Value = Environ("UserName")
Else
.TopLeftCell.Offset(, 1).ClearContents
End If
End With
End Sub

Related

VBA: ComboBox only showing one item after Workbook_Open event

I am attempting to have a Workbook_Open event populate a controls ComboBox
so that when the user goes to the Worksheet("Benchmarking"), they have a pre-populated list to choose from that includes all the items in the array datesArr.
The problem i am having is, upon opening the spreadsheet and navigating to the Worksheet("Benchmarking"), i am only seeing one item in the drop down list:
If i select that item then the list actually populates:
Desired result:
I want the full list to be available from the first time the user tries to make a selection not just after the ComboBox1_Change event is fired.
Having reviewed numerous post e.g. Sometimes the ActiveX Combobox only shows one row, why? , Populating Combo Box on WorkBook Open I have tried several different approaches including the following in the Workbook_Open event code:
.ListFillRange = "DropDownDates"
.List = DateArrToStrAr
I have also looped the array adding the items to ComboBox1. Each time i get the same 1 visible item in drop down result.
Is someone able to tell me where i am going wrong please?
My current code is
1) ThisWorkbook
Private Sub Workbook_Open()
With Worksheets("Benchmarking").OLEObjects("ComboBox1").Object
.Clear
.List = DateArrToStrArr '
End With
End Sub
2) Worksheet("Benchmarking"):
Private Sub ComboBox1_Change() 'QH 2/11/17
Dim datesArr() As String
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Lkup")
datesArr = DateArrToStrArr 'function that reads a named range of dates and converts to string to avoid dd/mm becoming mm/dd
If ComboBox1.Value = vbNullString Then ComboBox1.Value = "01/04/2016"
ComboBox1.List = datesArr
'.....other code
End Sub
Notes:
The array datesArr is populated by the function DateArrToStrArr() which reads in a named range of dates "DropDownDates" (workbook scope) and converts them to a string array. This is then assigned to the ComboBox.
DropDownDates is a dynamic named range with formula =OFFSET(Lkup!$F$16,,,Lkup!$M$12,)
Set-up: Excel 2016 64 bit Windows.
Thanks to #CLR for making me think about recalcs. I decided to hack my way around this with the following:
I have added in Worksheet("Benchmarking") a Worksheet_Activate event and removed the Workbook_Open code. This seems to do the trick
Private Sub Worksheet_Activate()
' ComboBox1.Clear
ComboBox1.List = DateArrToStrArr
End Sub

VBA with if case for checkbox in excel

I am having an userform where I have 8 Checkboxes in it.
Each checkbox is assigned to an call function called autofilter.
I would like to have an vba,in such a way that more than one Checkbox is used, then it should Display the result of selected Checkbox.
How can I achieve in VBA. I am struck how i should proceed with this Problem.
Expecting an help from Forum.
This is my autofilter program
Sub autofilter()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Result")
wslr = ws.Cells(Rows.Count, 1).End(xlUp).Row
Set myfilt = ws.Range("A1:AFU" & wslr)
myfilt.autofilter Field:=12, Criteria1:= _
"USA"
End Sub
similarly, i have them for other Locations as well till autofilter7.
Right now, i have the code working in such a way that, if check box 1 is true it calls autofilter1.
I would like to have a VBA, in such a way that, when i select 1 or more checkboxes, it should call their autofilter function together. How can i achieve this ?
[![I have userform with Checkboxes designed like this.in the command button i have the following code,
If CheckBox1.Value = True Then
Call autofilter
End If
similarly, I have it same for other checkboxes.
]1]1
Difficult to answer without all the exact details, but I think you are looking for something like:
In the command button _Click sub code, you should have this:
Edited : notice Dim i as String at the top.
Dim formControl As Control
Dim i As String
'loop through every control in the userform
For Each formControl In Me.Controls
'Test if the control is a checkbox
If LCase(TypeName(formControl)) = "checkbox" Then
If formControl.Value = True Then
'The below is very crude and you should find a better way of getting parameter from checkbox
'The below also assumes you use ONE filterFunction that takes a parameter
'You need to get the number from the checkbox, so take the number from the name of the checkbox
i = Right(formControl.Name, 1) - 1
'myFilterFunction i (Use this only if you have parameterised your function)
'change i to empty string if it was 0.
i = IIf(i = 0, "", i)
'This calls a function represented by the string
Application.Run "myFilterFunction" & i
End If
End If
Next formControl
At the moment, the away you've describe it, the code should work. Replace the name of the function with the name of your autofilter function....

Use VLOOKUP to pass cell reference to a public variable?

I have a userform that opens on cell change in a column.
That userform contains checkboxes, which all trigger a second userform with a text box which looks up a cell on a hidden sheet for its contents. (The checkbox that's ticked determines which cell the textbox looks for). The user then edits the box, clicks a button, and the new text is written back to the same cell.
This is the VBA for when the checkbox is ticked. It works great. Hooray!
Dim vln As Variant
Dim reta As Worksheet
Set reta = ActiveWorkbook.Sheets("RetailerActivity")
Set vln = ActiveCell.Offset(-1, -3)
UserForm2.TextBox1.Text = Application.WorksheetFunction.VLookup(vln, reta.Range("A1:Z100"), 3, False)
UserForm2.TescoSave.Visible = True
UserForm2.Show
End Sub
When the textbox has been edited, I would like to write it back to the same cell it came from. I figure the easiest way to do that is to have a public variable (as range), and to pass the result of the vlookup into that variable so the second userform can have a line which reads
Private Sub ASave_Click()
publicvariable.Value = TextBox1.Value
userform1.hide
End Sub
Nice and easy, rather than doing a VLookup again. Right?
Either way, I can't seem to set the public variable as the lookup.
Outside of any sub I have
Public bums As Range
And in the code above, after the bit where I've set the text box, I've tried to add the line
Set bums = Application.WorksheetFunction.VLookup(vln, reta.Range("A1:Z100"), 3, False)
But the code errors with a "type mismatch".
If I try
Set bums = Range(Application.WorksheetFunction.VLookup(vln, reta.Range("A1:Z100"), 3, False))
I get method "Range" of object "_global" failed.
I code by cobbling bits off the internet, as you can probably tell, so this is I don't doubt a complete kludge.
Any advice would be super appreciated.
VLookup returns a value, not a Range. You could use Match to find the row and then Cells to get the actual reference - for example:
Dim vMatch
vMatch = Application.Match(vln, reta.Range("A1:A100"),0)
If Not IsError(vMatch) then
Set bums = reta.Cells(vMatch, "C")
else
msgbox "No match for " & vln
Exit Sub
End If
Personally I would also not use a public variable, but create a property for Userform2 to which you can assign the range.

(VBA) Putting ComboBox + macroevent on cell change

I'm trying to put a combobox inside active worksheet (but not activeX combobox), choose a list to fill and linked cell. It is an easy task, for example:
Sub make_combobox()
ActiveSheet.DropDowns.Add(69.75, 1.5, 79.5, 40.5).Select
Selection.Name = "combo"
ActiveSheet.Shapes("combo").Select
With Selection
.ListFillRange = "$A$1:$A$3"
.LinkedCell = "$D$1"
.DropDownLines = 8
.Display3DShading = False
End With
End Sub
I tried to put macro in worksheet containing this combobox, which will show msgbox whenever chosen linked cell is changed according to the chosen option in combobox. I wrote this in Worksheet section:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("D1")) Is Nothing Then
MsgBox "It works!"
End If
End Sub
Unfortunately, it doesn't work (Actually, it works when I change a value in D1 manually, but not work as a result of change in combobox).
Just assign a macro to the control using the OnAction property. It will run after every change made to the Combobox's value.

Detecting changes to checkboxes via VBA

Following on from my previous question.
A requirement from the customer is to have checkboxes on a report to disable rows of information on another sheet. The rows are defined as named ranges, formated by P_XXXXXX. The XXXXXX is a unique identifier that is also a field on the row so I can easily generate the range names on the fly.
The problem I am having is:
After clicking on the items and then closing the form Excel asks if we want to save. This is undersirable.
I need someway of registering a change event happening on my generated checkboxes. So if one or more changes I can run through and hide/unhide the relevant ranges.
My code for adding the checkboxes looks like:
' For each row...
' check box in column 17(=Q).
Dim lCenter As Long
lCenter = rngCurrent.Width / 4 ' not actual centre but close enough
With ActiveSheet.CheckBoxes.Add(rngCurrent.Left + lCenter, rngCurrent.Top - 2, rngCurrent.Width, rngCurrent.Height)
.Interior.ColorIndex = xlNone
.Caption = ""
End With
So how do you link a change in a checkbox with a sub/function?
Set the OnAction property of the Checkboxes object to the name of a sub you want to run whenever the checkbox is checked or unchecked.
Sub MakeCB()
With ActiveSheet.CheckBoxes.Add(ActiveCell.Left + 0, ActiveCell.Top - 2, ActiveCell.Width, ActiveCell.Height)
.Interior.ColorIndex = xlNone
.Caption = ""
.OnAction = "CheckboxChange"
End With
End Sub
Sub CheckboxChange()
MsgBox "change"
End Sub
I don't think there are any events available with the Excel.Checkbox control. Try using the MSForms checkbox instead. You'll need a reference to 'Microsoft Forms 2.0 Object Library' - it's not redistributeable, but if you're using VBA, then that's fine.
You can then do something like this, and handle the event in the usual way:
''class level
Private WithEvents m_Checkbox as MSForms.CheckBox
Public Sub MakeCheckbox()
Set m_Checkbox = Activesheet.OLEObjects.Add("Forms.Checkbox.1")
End Sub
Private Sub m_Checkbox_Click()
''Do stuff
End Sub
Obviously, you'll only be able to handle a set number of checkboxes this way - I would recommend creating a class to hold each checkbox.