Methods and Variable - vba

Is it possible to use varibles within a VBA method?
For eg, i'm trying to add item to a list: Listbox1.AddItem (item1)
Qns: Am I able to replace "1" with a varible: Listbox & "Varible".AddItem (item1)
Sub ThisWorks()
Worksheets("Control Sheet").Tx_TgtRaw_FX_CA.AddItem ("Remark1")
End Sub
=======================
Sub test()
'Trying to use this as a varible instead
X = "Tx_TgtRaw_FX_CA"
Worksheets("Control Sheet").X.AddItem ("Remark1")
'Error 438: Object does not support this property or method
End Sub
=======================
Sub testarr()
Dim y(0 To 2)
Set y(0) = "Tx_TgtRaw_FX_CA"
Worksheets("Control Sheet").y(0).AddItem ("Remark1")
'Error 438: Object does not support this property or method
End Sub

I don't think that any way to use INDIRECT variable names in VBA would be possible.
One workaround is to define arrays and refer to their items, e.g.:
Dim ListBoxes(10) as Variable
Set ListBoxes(1) = ListBox1
Set ListBoxes(2) = ListBox2
(instead of preparing the array manually you can
also add the items programmatically to the form)
...
ListBoxes(i).AddItem(item1)

You'd need to loop the controls in the appropriate container to check if its name equals the control name you need. I.e.
For Each c in Controls
If LCase(c.Name) = LCase("NameOfDesiredControl") Then
... Work with it ...
End If
Next
This can easily be translated into a function.

I'm not sure exactly what your goals are, but the following modifications to your posted code allows it to work:
Option Explicit
Sub ThisWorks()
Worksheets("Control Sheet").Tx_TgtRaw_FX_CA.AddItem ("Remark1")
End Sub
'=======================
Sub test()
Dim X As Object
'Trying to use this as a varible instead
Set X = Worksheets("Control Sheet").Tx_TgtRaw_FX_CA
X.AddItem ("Remark1")
End Sub
'=======================
Sub testarr()
Dim y(0 To 2)
Set y(0) = Worksheets("Control Sheet").Tx_TgtRaw_FX_CA
y(0).AddItem ("Remarkx")
End Sub

Related

Trying to use a variable in a Set statement

I have an array oNam of TextBox names, I want to call a sub and pass an index number pointing to the specific TextBox I am working on to the sub. In the sub I use the following code, Dim MyControl As Control, and Set MyControl = Me![oNam(n)]. I get the following
Run-time error 2147024809: Could not find the specified object.
However, if I replace oNam(n) with the actual TextBox name, Me![tbxBuyNowPrice], I don't get the error. I included a Debug.Print oNam(n) and it holds the correct name.
I've replaced the variable with the actual name and it works.
Dim oNam() As Variant
....Call TextBoxControl(34)
Sub TextBoxControl(n As Integer)
Debug.Print oNam(n) <====Shows correct name
Set MyControl = Me![oNam(n)] <====GET THE ERROR HERE
If DVAL(n) = "Yes" Then
Call TextBoxSettings(MyControl, "", vbYellow)
MyControl.SetFocus
GoTo EndOfTextBoxControl
Else
Call TextBoxSettings(MyControl, 0, vbCyan)
End If
EndOfTextBoxControl:
End Sub

check if textbox exists vba (using name)

I am using Ms-Access and I created a userform which has a number of Textboxes on it. The boxes are named: Box1, Box2, Box3 ...
I need to loop through all boxes, but I don't know which is the last one. To avoid looping through all userform controls I thought of trying the following:
For i =1 To 20
If Me.Controls("Box" & i).value = MyCondition Then
'do stuff
End If
Next i
This errors at Box6, which is the first box not found. Is there a way to capture this error and exit the loop when it happens.
I know I could use On Error but I 'd rather capture this specific instance with code instead.
Thanks,
George
A Controls collection is a simplified collection of controls (obviously) and share a same order as a placement order of controls.
First of all, even a creatable collection object lacks methods such as Exists or Contains , hence you need a function with error handling to checking/pulling widget from a collection.
Public Function ExistsWidget(ByVal Name As String) As Boolean
On Error Resume Next
ExistsWidget = Not Me.Controls(Name) Is Nothing
On Error GoTo 0
End Function
If you really doesnt like "ask forgiveness not permission" option you can pull entire ordered collection of your textboxes (and/or check existance by name in another loop with similar logic).
Public Function PullBoxes() As Collection
Dim Control As MSForms.Control
Set PullBoxes = New Collection
For Each Control In Me.Controls
If TypeOf Control Is MSForms.TextBox And _
Left(Control.Name, 3) = "Box" Then
Call PullBoxes.Add(Control)
End If
Next
End Function
Since names of widgets are unique - you can return a Dictionary from that function with (Control.Name, Control) pairs inside and able to check existance of widget by name properly w/o an error suppression.
There's a good guide to Dictionary if it's a new information for you.
Anyway, no matter what object you choose, if user (or code) is unable to create more of thoose textboxes - you can convert this Function above to a Static Property Get or just to a Property Get with Static collection inside, so you iterate over all controls only once (e.g. on UserForm_Initialize event)!
Public Property Get Boxes() As Collection
Static PreservedBoxes As Collection
'There's no loop, but call to PullBoxes to reduce duplicate code in answer
If PreservedBoxes Is Nothing Then _
Set PreservedBoxes = PullBoxes
Set Boxes = PreservedBoxes
End Property
After all, the last created TextBox with name Box* will be:
Public Function LastCreatedBox() As MSForms.TextBox
Dim Boxes As Collection
Set Boxes = PullBoxes
With Boxes
If .Count <> 0 Then _
Set LastCreatedBox = Boxes(.Count)
End With
End Function
I think that now things are clearer to you! Cheers!
Note: All code are definitely a bunch of methods/properties of your form, hence all stuff should be placed inside of form module.
Long story short - you cannot do what you want with VBA.
However, there is a good way to go around it - make a boolean formula, that checks whether the object exists, using the On Error. Thus, your code will not be spoiled with it.
Function ControlExists(ControlName As String, FormCheck As Form) As Boolean
Dim strTest As String
On Error Resume Next
strTest = FormCheck(ControlName).Name
ControlExists = (Err.Number = 0)
End Function
Taken from here:http://www.tek-tips.com/viewthread.cfm?qid=1029435
To see the whole code working, check it like this:
Option Explicit
Sub TestMe()
Dim i As Long
For i = 1 To 20
If fnBlnExists("Label" & i, UserForm1) Then
Debug.Print UserForm1.Controls(CStr("Label" & i)).Name & " EXISTS"
Else
Debug.Print "Does Not exist!"
End If
Next i
End Sub
Public Function fnBlnExists(ControlName As String, ByRef FormCheck As UserForm) As Boolean
Dim strTest As String
On Error Resume Next
strTest = FormCheck(ControlName).Name
fnBlnExists = (Err.Number = 0)
End Function
I would suggest testing the existence in another procedure per below: -
Private Sub Command1_Click()
Dim i As Long
i = 1
Do Until Not BoxExists(i)
If Me.Conrtols("Box" & i).Value = MyCondition Then
'Do stuff
End If
i = i + 1
Next
End Sub
Private Function BoxExists(ByVal LngID As Long) As Boolean
Dim Ctrl As Control
On Error GoTo ErrorHandle
Set Ctrl = Me.Controls("BoX" & LngID)
Set Ctrl = Nothing
BoxExists = True
Exit Function
ErrorHandle:
Err.Clear
End Function
In the above, BoxExists only returns true if the box does exists.
You have taken an incorrect approach here.
If you want to limit the loop, you can loop only in the section your controls reside e.g. Detail. You can use the ControlType property to limit controls to TextBox.
Dim ctl As Control
For Each ctl In Me.Detail.Controls
If ctl.ControlType = acTextBox Then
If ctl.Value = MyCondition Then
'do stuff
End If
End If
Next ctl
I believe the loop will be faster than checking if the control name exists through a helper function and an On Error Resume Next.
But this only a personal opinion.

VBA: Assign a generic OLEObject to a CheckBox variable

My code loops through all of the existing OLEObjects within the current sheet in an Excel workbook. I want it to find a specific one (based on the name passed to the sub) which is always a CheckBox and assign it to a variable of type CheckBox.
Note: The other objects are not all Checkboxes, hence the generic OLEObject type.
Example code that calls sub, showing example of name:
HandleCheckBoxClick("chkAddSummary")
Sub that looks for this specific object:
Sub HandleCheckBoxClick(nm As String)
Dim o As OLEObject
Dim cb As CheckBox
For Each o In Me.OLEObjects
If o.name = nm Then
Set cb = o
End If
Next o
End Sub
I found a very similar question at: Excel VBA: how to cast a generic control object into a ComboBox object? but it refers to Form Controls (not ActiveX Controls). I tried the method given in the answer to see whether it was transferrable between the two control types but had no success.
The reason I want to do this is similar to the asker of the question I refer to - I cannot use methods like CheckBox.Value with a generic OLEObject variable.
I have also tried using the OLEObject.progID method to make sure o is a checkbox object. The error I get when trying to Set cb = o is a Type Mismatch.
When declared as MSForms.CheckBox it then should be possible to assign o.Object.
Sub test()
HandleCheckBoxClick "chkAddSummary"
End Sub
Sub HandleCheckBoxClick(nm As String)
Dim o As OLEObject
Dim CB As MSForms.CheckBox
For Each o In Me.OLEObjects
If o.Name = nm Then
Set CB = o.Object
End If
Next o
End Sub
I couldn't find (so far) how to assign it to a CheckBox, I found how to assign it to a OLEObject variable (if that helps you).
Sub HandleCheckBoxClick(nm As String)
Dim o As OLEObject
Dim CB As CheckBox
Dim oleObj As OLEObject
' loop though all OLEObjects in "Sheet2" >> modify to your sheet name
For Each o In Sheets("Sheet2").OLEObjects
If TypeName(o.Object) = "CheckBox" Then
Debug.Print o.Name
' if you have more then 1 CheckBox
If o.Name = nm Then
Set oleObj = o
' Set CB = o.Select
End If
End If
Next o
' just to test the options with OLEObject variable
With t
t.Top = 100
If t.Object.Value = True Then
MsgBox "CheckBoX " & nm & " is selected"
Else
MsgBox "CheckBoX " & nm & " is not selected"
End If
End With
End Sub
This worked for me to get the checkbox state
Dim checkBox1 As Object
Set checkBox1 = Sheet1.OLEObjects("CheckBox1").Object
MsgBox checkBox1.Value

Removing Macro from Shape Programmatically

I have several "Buttons" that change dynamically with the content of the sheet. I just need to figure out 1 line of code to get it working properly (Line 3):
Public Sub ClearMacro(shapename As String)
On Error Resume Next
ActiveSheet.Shapes(shapename).OnAction = Nothing
End Sub
I want to completely remove the macro from the shape, but keep the shape. Anything I can do differently to make this work?
Use Set and Nothing on objects. OnAction accepts a string value, use .OnAction = "" instead.
Public Sub ClearMacro(shapename As String)
On Error Resume Next
ActiveSheet.Shapes(shapename).OnAction = ""
End Sub

How do I refer to a controls object, on a worksheet, using a variable name?

I have added a ListBox to a SHEET (not to a "UserForm")
I did this using the mouse.
I clicked the little Hammer and Wrench icon.
This ListBox seems to be easily referenced using code such as this:
ListBox1.Clear
or
ListBox1.AddItem("An option")
However, I have three of these ListBoxes (named, conveniently, ListBox1, ListBox2, and ListBox3) and I want to write a function to populate them with array data, like this:
Call populate_listbox(ListBox2, designAreaArray)
Where the first argument is the listbox name, the 2nd is the data.
But I do not know how to send "ListBox2" correctly, or refer to it correctly within the function.
For example:
Dim controlName as string
controlName = "ListBox1"
doesn't work, even if I define the function as follows:
Sub populate_listbox(LB As ListBox, dataArray As Variant)
Dim i As Integer: i = 0
For i = LBound(dataArray, 2) + 1 To UBound(dataArray, 2) ' Skip header row
LB.AddItem (dataArray(index, i))
Next i
End Sub
Clearly it results in a mis-matched data type error. I've tried defining "controlName" as a ListBox, but that didn't work either...
Though perhaps it is my reference to the listBox that is incorrect. I've seen SO MANY ways to refer to a control object...
MSForms.ListBox.
ME.ListBox
Forms.Controls.
Worksheet.Shapes.
The list goes on an on, and nothing has worked for me.
Try this:
Dim cMyListbox As MSForms.ListBox
Set cMyListbox = Sheet1.ListBox1 '// OR Worksheets("YourSheetName").Listbox1
cMyListbox.AddItem("An option")
Also you can populate a listbox without having to loop through the array, try this:
Dim cMyListbox As MSForms.ListBox
Dim vArray As Variant
Set cMyListbox = Sheet1.ListBox1
vArray = Range("A1:A6").Value
cMyListbox.List = vArray
Change the sub signature to match this:
Sub populate_listbox(LB As MSForms.ListBox, dataArray As Variant)
Now you can pass it like you were trying to originally.
NOTE: This only works if you used the "ActiveX" version of the listbox. I'm assuming you are because you are able to call ListBox1 straight from a module.
PS: The ActiveX controls are members off of the parent sheet object. So if you have the listbox1 on sheet1, you can also call it like Sheet1.ListBox1 so you don't get confused if you end up with multiple sheets with multiple listboxes. Also, you may want to change the name just to make it easier on yourself.
Dim controlName As OLEObject
Set controlName = Sheet1.OLEObjects("ListBox1")
Call populate_listbox(controlName, designAreaArray)
Sub populate_listbox(LB As OLEObject, dataArray As Variant)
Dim i As Integer: i = 0
For i = LBound(dataArray, 2) + 1 To UBound(dataArray, 2) ' Skip header row
LB.Object.AddItem (dataArray(Index, i))
Next i
End Sub
To access the state of a checkbox Active-X control on Sheet1:
Dim checkBox1 As Object
Set checkBox1 = Sheet1.OLEObjects("CheckBox1").Object
MsgBox checkBox1.Value