I'm currently working with forms in VBA, and want to use one form to modify some values in another form. So I have Form1, which has three ListBoxes that hold a bunch of items each. Then I have Form2, which has a non-modifiable TextBox that will contain the value of the selected ListBox item that the user wants to edit.
However, since I have multiple ListBoxes, I want to know which ListBox I last clicked on, so that I can draw the selected item from that list box and edit that item when the user clicks "Apply" in Form2.
I've looked created a property that will keep track of the name of the last ListBox. Thing is, I'm having trouble using it. Here is my code:
Public Property Get LastClicked() As ListBox
LastClicked = LastListBox
End Property
Public Property Let LastClicked(boxName As ListBox)
LastListBox = CStr(boxName)
End Property
Private Sub FirstNameTextBox_Change()
If (FirstNameTextBox.ListIndex <> -1) Then
EditButton.Enabled = True
Else
EditButton.Enabled = False
End If
End Sub
Private Sub FirstNameTextBox_Click()
LastClicked (FirstNameTextBox)
End Sub
Private Sub LastNameTextBox_Click()
LastClicked (LastNameTextBox)
End Sub
When I attempt to set the property with the name of the listbox, it brings back an error:
"Invalid use of property"
I assume this means i'm passing in the wrong value, but I don't know what other value to pass in. I'd appreciate any help I can get on this.
2 problems. First, LastClicked is a property, not a method. That means you need to assign it a value, not pass it a parameter. Second, properties that expose Objects need to use Property Set instead of Property Let. Property Let is only for primatives. Try something like this:
Option Explicit
Private LastListBox As ListBox
Public Property Get LastClicked() As ListBox
Set LastClicked = LastListBox
End Property
Public Property Set LastClicked(boxName As ListBox)
Set LastListBox = boxName
End Property
Private Sub FirstNameTextBox_Change()
If (FirstNameTextBox.ListIndex <> -1) Then
EditButton.Enabled = True
Else
EditButton.Enabled = False
End If
End Sub
Private Sub FirstNameTextBox_Click()
Set LastClicked = FirstNameTextBox
End Sub
Private Sub LastNameTextBox_Click()
Set LastClicked = LastNameTextBox
End Sub
Related
I might miss just a stupid small detail but I don't get a hang on it.
I've created a userform with a listbox where I want the user to select one item. This is working so far as my variable "termin" has the right value before I close the user form
Private Sub OKButton_click()
termin = Eventlist.List(Eventlist.ListIndex)
MsgBox termin 'Just for testing purposes. It gives me the selected item
Unload Eventabfrage
End Sub
And this is a part of what I have in 'ThisOutlookSession':
Option Explicit
Dim termin As String
Public Sub MailMerge()
Eventabfrage.Show
MsgBox termin 'and there it is empty but shouldn't be empty
enter code here
End Sub
What do I have to do to hand over the value to my MailMerge Sub?
It is a macro in Outlook so storing it in any Excel cell is not an option.
Other than using Public variable (which I would only as the last chance since it's prone to many drawbacks), you can use UserForm class Tag property to pass info in and out an instance of a Userform and a more safe Userform instantiating and terminating scheme, as follows:
in the calling module:
Public Sub MailMerge()
Dim termin As String ' declare 'termin' as a Sub scoped variable
With New Eventabfrage ' get a new instance of the wanted userform class and reference it
.Show
termin = .Tag ' retrieve referenced userform 'Tag' property and store it in 'termin' variable
'enter code here
End With ' <-- this will unload the userform instance
End Sub
in the Eventabfrage class module
Private Sub OKButton_Click()
With Me ' reference the Userform class current instance
.Tag = .Eventlist.List(.Eventlist.ListIndex)
.Hide ' hide the userform instead of unloading it, so as to have it "alive", along with its properties (and methods) in its calling sub
End With
End Sub
I have a Userform with multiple Textboxes. All the Textboxes are in tab-order.
Each subsequent Textbox is back colored yellow to indicate to the user which is the next Textbox to complete.
If the Textbox Value is determined to be invalid I want the control to SetFocus back on that particular Textbox. However, control is automatically handed over to the next Textbox in the tab-order.
When I try to focus back on the required Textbox with the mouse, this fires an event on the next Textbox, which follows the rules of my program and requests the user to enter a valid value.
Below is a sample of two Textboxes, if the user fails to enter a first name I want the control to return to the tbxCustomerFirstName Textbox, however, the control is handed over to the tbxCustomerSurName Textbox, even though I've "tbxCustomerFirstName.SetFocus".
AstFlag = 2 means there has to be a valid value in the Textbox.
AstFlag = 1 means the Textbox value can be blank.
I stepped through the program and AstFlag does indeed = 2, and the set focus code is executed.
'====================================================================================
'
' Customer First Name
'
Private Sub tbxCustomerFirstName_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Call tbxValues(3)
If tbxCancel = True Then
Cancel = True
End If
If AstFlag = 2 Then
tbxCustomerFirstName.SetFocus
End If
End Sub
'====================================================================================
'
' Customer Surname
'
Private Sub tbxCustomerSurName_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Call tbxValues(4)
If tbxCancel = True Then
Cancel = True
End If
If AstFlag = 2 Then
tbxCustomerSurName.SetFocus
End If
End Sub
You want to set the Cancel parameter to True when you mean to cancel exiting the control.
I don't know what your tbxCancel flag logic is, but...
If AstFlag = 2 Then
theControlYouAreExiting.SetFocus
End If
Should be
If AstFlag = 2 Then
Cancel.Value = True
End If
That will prevent exiting the control, so there's no need to SetFocus on anything. Now again I don't know what tbxCancel is supposed to be doing, but it doesn't look right.
In fact I would scrap all this painfully duplicated conditional logic, and implement some Validate method that returns True if the field is valid (and thus can be exited) and False if the field is invalid (and thus Cancel.Value must be True and focus remains on the control).
Cancel.Value = Not Validate(args)
Where args could be theControlYouAreExiting.Tag, and then you could use the controls' Tag property to hold the metadata you're likely currently hard-coding in tbxValues.
Ultimately the problem you're solving is going to create a mess no matter how you put it, because everything is happening in the form's code-behind: you want to separate the View (the form/UI) from the Model (the data it's manipulating).
The solution is to create a class module that represents your model. This model exposes a property that each control on your form manipulates, and then the model itself knows how to validate each property.
Option Explicit
Private model As CustomerModel
Private Sub UserForm_Initialize()
Set model = New CustomerModel
End Sub
Public Property Get CustomerModel() As CustomerModel
Set CustomerModel = model
End Property
Public Property Set CustomerModel(ByVal value As CustomerModel)
Set model = value
End Property
Private Sub tbxCustomerSurName_Change()
model.Surname = tbxCustomerSurName.Text
End Sub
Private Sub tbxCustomerSurName_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Cancel.Value = Not model.IsValidSurname
End Sub
And with that you don't need to maintain flags and state switches to keep track of what the metadata is for the control you're in.
I have a UserForm with this function:
Public MyVariable As String
Private Sub UserForm_Initialize()
[...my code...]
End Sub
To call my Userform from a button i do:
Sub CallUserForm_Appro()
UserForm1.MyVariable = "Appro"
UserForm1.Show
End Sub
Sub CallUserForm_User()
UserForm1.MyVariable = "User"
UserForm1.Show
End Sub
My goal is to remove "Label1" if user click on button to call CallUserForm_Appro()
So, i tried in UserForm_Initialize() to do:
Public MyVariable As String
Private Sub UserForm_Initialize()
[...my code...]
If MyVariable = "Appro" Then
UserForm1.Controls.Remove "Label1"
End If
End Sub
I have no error but my Label1 is always visible.
This is how you set the visibility of the label to false:
UserForm1.label1.Visible = false
Then it should not be visible any more.
The `Initialize event occurs before the variable is set (because you can't access any property of the form without it being loaded first).
You should use the Activate event instead as long as the control is added at run time. If it's a design time control, you can't delete it, only hide it. Alternatively, you might only add it to the form if the variable is not set to "Appro"
I have a userform with a lot of emailaddresses. I want the user to be able to select who to send an email to. I do so with checkboxes which are created on run time. To make it easier to use, I have also added a checkbox which allows the user to select of deselect all checkboxes.
This works perfectly as I want it to, but there is one problem I'm breaking my head over. If all checkboxes are checked and one gets unchecked, I want the "Select all" checkbox to be unchecked as well - and vice versa, if not all checkboxes were checked and the final checkbox is being checked by the user I want the "Select all" checkbox to be checked as well.
I try to do this using a class module. My overall knowledge of vba is pretty okay, but class modules are new territory for me, so excuse me if my language gets a bit fussy now.
In the initialize event of the userform I create a new collection and I assign clicks to these specific checkboxes. That works perfectly, as it doesn't give any errors in the initialize event of the userform and an event is triggered once I click one of these checkboxes. The problem I'm having is that I can't get a grip on the "Select all" checkbox (chkSelAll) in the userform. I've tried creating a public object for this checkbox in the userform (Public objSelAll As MSForms.CheckBox), but still it gives me the "Variable not defined" error once I click one of the checkboxes.
Here's the code for the class module (cls_RIRI):
Private WithEvents chkBox As MSForms.CheckBox
Public Sub AssignClicks(ctrl As Control)
Set chkBox = ctrl
End Sub
Private Sub chkBox_Click()
If chkBox.Value = False Then objSelAll.Value = False
'^This is where the error occurs: variable not defined
End Sub
And here's the relevant part of the Userform_Initialize event:
Private colTickBoxes As Collection
Public objSelAll As MSForms.CheckBox
Private Sub UserForm_Initialize()
Dim ChkBoxes As cls_RIRI
Dim ctrl As Control
Set objSelAll = Me.Controls.Item("chkSelAll")
Set colTickBoxes = New Collection
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" And Left(ctrl.Name, 1) = "M" Then
Set ChkBoxes = New cls_RIRI
ChkBoxes.AssignClicks ctrl
colTickBoxes.Add ChkBoxes
End If
Next ctrl
Set ChkBoxes = Nothing
Set ctrl = Nothing
End Sub
As you can see I didn't get to the point yet where I let the code check if all checkboxes are checked so that the select all checkbox can be checked as well. I'm not really looking for this code, I'll probably manage it once I get grip on the select all checkbox from the class module, so please don't worry about this part! :)
Private WithEvents chkBox As MSForms.CheckBox
private strParentFormName as string
Public Sub AssignClicks(ctrl As Control,strFormName as string)
strParentFormName=strFormName
.....
end sub
Private Sub chkBox_Click()
dim f as userform
set f=userforms(0) <--- or loop the userforms to get form name
If chkBox.Value = False Then f.controls("objSelAll").Value = False
'^This is where the error occurs: variable not defined
End Sub
and something like this
Public Function GET_USERFORM(strUserform As String) As UserForm
Dim i As Integer
For i = 0 To UserForms.Count - 1
If UserForms(i).Name = strUserform Then
Set GET_USERFORM = UserForms(i)
Exit For
End If
Next i
End Function
You'll need to pass in the form also, as the variable doesnt exist in the class. Add a property for the formname or if you'll only have 1 form open, then use the open form or reference the form if multiples are open. Your class exists on it's own as a check box. I am not sure, but you may be able to get the parent object from the checkbox. Hope this helps.
private strFormName as string
Public Property Let ParentForm(value as string)
strFormname=value
End Property
then...
userforms(strFormname).controls("objSelectAll").value=true
I have the following problem:
Private Sub TextBox1_Change()
Control (this) <<<<----- this is Empty
End Sub
Private Sub TextBox2_Change()
Control (this) <<<<----- this is Empty
End Sub
Private Sub TextBox3_Change()
Control (this) <<<<----- this is Empty
End Sub
Public Sub Control(asdf As MSForms.TextBox)
asdf.Font.Size = 11
asdf.Font.Bold = True
End Sub
The compiler says that 'this' is empty. What should I put there to recognize the TextBox?
Thx
When you use parentheses in your code you're evaluating Me.TextBox1, which ends up passing a String to Control. If you drop the parens it will work.
Private Sub TextBox1_Change()
Control Me.TextBox1 'without the parens
End Sub
Typically you don't use () when calling a Sub, unless you're using Call
If you end up having a lot of textboxes, it may be easier to customize the event handler in a custom class.
In a standard module, put a globally scoped collection variable
Public gcolTextboxes As Collection
Create a custom class module called CTbxEvents
Private WithEvents mtb As MSForms.TextBox
Public Property Get tb() As MSForms.TextBox
Set tb = mtb
End Property
Public Property Set tb(otb As MSForms.TextBox)
Set mtb = otb
End Property
Private Sub mtb_Change()
tb.Font.Size = 11
tb.Font.Bold = True
End Sub
Finally, in ThisDocument, load up all the textboxes when the document opens.
Private Sub Document_Open()
Dim f As Field
Dim clsTbxEvents As CTbxEvents
'Globally scoped collection to hold the classes
Set gcolTextboxes = New Collection
'Loop throught the fields
For Each f In Me.Fields
'Only fields that are activex controls
If f.Type = wdFieldOCX Then
'only activex controsl that are textboxes
If TypeOf f.OLEFormat.Object Is MSForms.TextBox Then
'create a new class, add the textbox, add to collection
Set clsTbxEvents = New CTbxEvents
Set clsTbxEvents.tb = f.OLEFormat.Object
gcolTextboxes.Add clsTbxEvents
End If
End If
Next f
End Sub
Now any textbox you add will use the same event handler and you don't keep having to call a separate sub. If you truly only have three textboxes, it's probably overkill.
The this keyword is specific to C++/C#/Java, the corresponding keyword for VB/VB.NET is Me