Passing text box control to private sub/function - vba

I have a series of text boxes that I want to evaluate on form load to change some other elements of the form. Rather than processing them line by line I want to create a function. How can I pass the name of the text box to a function. For example:
Private Sub Form_Load()
Call SetIntialToggleValue(MyTextboxName)
End Sub
Private Sub SetIntialToggleValue(ByRef ControlName As Control)
MsgBox ControlName.Value
End Sub
This give me "Object required" error on the MsgBox line. Which makes me think I'm not passing the textbox control properly.

I would ask for clarification if I had enough rep to comment, but here are some thoughts:
If you haven't explicitly declared your variable, MyTextboxName, it will be of type variant. You can't pass a variant to an object parameter. This might be enough:
dim MyTextBoxName as Control
But in this case, you wouldn't be working with names at all. If you want to work with names, you have to change your function. Change the signature to take a string and then use the string as the index into the Controls collection (if there is one on Access Forms):
Private Sub SetIntialToggleValue(ByRef ControlName As String)
MsgBox Form.Controls(ControlName).Value
End Sub

How about something like the following:
Option Explicit
Dim ctl as Control
Private Sub Form_Load()
set ctl = Me.MyTextboxName
Call SetIntialToggleValue(ctl)
End Sub
Private Sub SetIntialToggleValue(ByRef ControlName As Control)
MsgBox ControlName.Value
End Sub

I found the answer here. You can declare:
Private Sub SetIntialToggleValue(ByRef ControlName As MSForms.TextBox)

Currently, you're not passing in the name of the textbox. You're passing in the textbox object itself. You did reference it as a Control in your method, but if you're only using textboxes, you could replace Control with Textbox. Indeed, Textbox is its own type.

The answer as I'm sure you have all figured out is given here:
https://www.access-programmers.co.uk/forums/threads/passing-a-control-as-parameter.213322/
Calling a function with brackets causes the compiler to force a conversion to ByVal which replaces your "control" with the "value" of the control and passes it with the wrong type.
Correct answer is simply SetIntialToggleValue MyTextboxName

Related

Access VBA put a form element name as an argument

Let's see if anyone knows how to solve this problem:
I have a form with several elements: Some of them are textboxes called A1, A2, A3, A4...
Now, their AfterUpdate SubProcedure is extremely long but barely similar for each of them: A1_AfterUpdate, A2_AfterUpdate, A3_AfterUpdate...etc... are very similar but for the names of the textboxes they change.
My idea was to gather all that was equal in a subprocedure defined this way:
Private Sub Update(Box As String, Menu As Boolean)
If Menu=True{
Me!Box.Text = "This is the text that is going to change"
}
End Sub
So, the only thing I must do is to call it this way, for instance:
Update(A1, True)
But it doesn't seems to work. Any idea on how to reach this objective?
Add a class module - I've called it clsTextBoxEvents.
Add this code to the class:
Public WithEvents txt As Access.TextBox
Private Sub txt_AfterUpdate()
MsgBox txt.Name & " has been updated."
End Sub
In your form module add this code:
Public MyTextBoxes As New Collection
Private Sub Form_Open(Cancel As Integer)
Dim ctl As Control
Dim txtBoxEvent As clsTextBoxEvents
For Each ctl In Me.Controls
If TypeName(ctl) = "TextBox" Then
Set txtBoxEvent = New clsTextBoxEvents
Set txtBoxEvent.txt = ctl
txtBoxEvent.txt.AfterUpdate = "[Event Procedure]"
MyTextBoxes.Add txtBoxEvent
End If
Next ctl
End Sub
The MyTextBoxes declaration must be at the very top of the module.
This just adds the AfterUpdate event to all textboxes on the form. You'll probably want to refine that a bit to textboxes with specific text in the name, or controls that are in a specific frame on the form.
If you use a function instead of a sub:
Private Function UpdateCtl(Menu As Boolean)
If Menu Then
activecontrol = "This is the text that is going to change"
End If
End Sub
then you can call it directly from the control's AfterUpdate property: =UpdateCtl(True).
Simple and fast

Set values on User Form using form properties

Appologies if this has been asked already, but I cannot seem to find an answer. I want to set a do something on a UserForm as it is loaded up based on the value passed to it from a property. What I have is a button on an Excel worksheet which loads the user form as follows:
Sub button()
Dim fm As New UserForm1
fm.ValueToPass = "Hello"
fm.Show
End Sub
Behind the form is the following code:
Private myString As String
Public Property Let ValueToPass(ByVal x As String)
myString = x
End Property
Private Sub UserForm_Initialize()
If myString = "Hello" Then
'Do something on my form
else
'Do something else on my form
end if
End Sub
The problem is that when the form is loaded up, the myString is empty. I believe that the reason is that the form is initialised before the property ValueToPass is set. What is the best solution to this?
You probably think that when you call:
fm.ValueToPass = "Hello"
in Private Sub UserForm_Initialize() it is possible that the myString value is passed before initializing the form. E.g., you are somehow expecting that myString can be something different than "" (thus you have the condition). This is not the case - first the _Initialize constructor is executed and then anything else is carried out:
With your code, you need somehow to tell the form, that it should update its label. Consider this inside the form:
Public Sub ShowForm()
Me.Label1 = myString
Me.Show
End Sub
Then in the module, call it like fm.ShowForm instead of fm.Show.
Actually, it is a good idea, if you work with userforms, following the Model-View-Controller pattern. For this you would need a separate class.
Userform closes after "End Sub" without ever calling "Unload Me"
Easiest way:
Sub button()
Dim fm As New UserForm1
fm.Label1.Caption = "Hello"
fm.Show
End Sub
you should be using the initialize event for this:
Sub OpenForm()
MyForm.show
end sub
Then in the initialize event of the userform, put this:
Me.LabelName.Caption = "Your label text"
(double-click the userform to view its code, then select from the top right drop-down "Initialize".)

Treat Userform as a Function

I have created a VBA UserForm which is essentially a glorified input box
Just like an input box can be employed like this
Dim returnVal As String
returnVal = InputBox("Write some string")
I would like my userform to run like this
Dim returnVal As customClass
Set returnVal = MyUserForm([some arguments])
ie. the MyUserForm() code passes some arguments to the userform, and when the userform is closed, it gets some arguments back (in the form of a custom class rather than a plain string)
What's the best way of structuring my userform to allow this functionality?
Currently, I'm just declaring some variables and the custom class publicly. I'm catching command button clicks and Query_close() events to hide the form, then I read the outputVal and close the form completely. I don't like this because I'd like my form to be totally self contained, and I think the capturing of events is messy.
In simplified code (read/return a string):
Function myUf(inVal As String) As String
Dim frm As New frmTest
frm.inputval = inVal
frm.Init 'sets caption. We cannot rely on userform initialize as this runs before inputval is set
'We could pass a variable here to save writing to the public variable
frm.Show
myUf = frm.outputVal
Set frm = Nothing
End Function
And in my Userform called frmTest with a textbox called tb1
Public inputval As String
Public outputVal As String
Public Sub Init()
Me.Caption = inputval 'setting caption, but could pass this anywhere
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode <> 1 Then Cancel = 1
outputVal = tb1 'reading value from textbox, but could return anything here
Me.Hide
End Sub
You need to find a way to initiate the UserForm from a ClassObject. Then, you can use a simple factory pattern to create the UserForm exactly the way you want.
In general, I have copied a bit of the code of Mat's Mug somewhere in StackOverflow and I wrote an article about the User Forms. If you take a look here (http://www.vitoshacademy.com/vba-the-perfect-userform-in-vba/) you will find a way to initialize the form with Public Sub ShowMainForm() It's possible to add a parameter to the ShowMainForm, then pass it to the initializer of the class.
In general, take the code from the article, make sure it works, and change the ShowMainForm initializer to the following:
Public Sub ShowMainForm(strText As String, strText2 As String)
If (objPresenter Is Nothing) Then
Set objPresenter = New clsSummaryPresenter
End If
objPresenter.Show
Call objPresenter.ChangeLabelAndCaption(strText, strText2)
End Sub
Then, if you call like this in the immediate window:
call ShowMainForm("Just","testing")
You will get this:
Which is quite what you need. :)
The basic idea is:
Create a Function in witch you combine your arguments to a string like:
strOpenArg = "param1:=value1;param2:=value2;"
than open the form with the OpenArgs
DoCmd.OpenForm "UserForm", acNormal, , , , acDialog, strOpenArgs
get your value and close the Form
Value= Form_UserForm.Value
DoCmd.Close acForm, "UserForm", acSaveNo
in the UserForm set Form_open. Here you can get your parameters.you can devide this by string splitting.
Set also an OK Button, where you make the form just invisible and set the return value
Private Sub Form_Open(Cancel As Integer)
Dim strParameter as String
strParameter = Me.OpenArgs 'Here are your parmeters
End Sub
Private Sub ok_Click()
m_Value = "Your ReturnValue"
Me.Visible = False
End Sub
Private m_Value As String
Public Property Get Value() As String
Value = m_msgBoxResult
End Property
There's no way to one-liner the code like you want to, unfortunately. If all your userform code is self-contained then the only way for it to pass values out is to change the values of public variables. Mat's Mug's answer here is the layout I usually use when trying to simulate functions like 'InputBox' but you still can't get it in one line without writing a separate function. Using userform properties allows you to contain more of your code within the form itself.

VBA Force variable affectation by reference

So I encouter some very weird problem in VBA which I believe is a "feature" of the language.
Right now the following code generates an error of type incompatibility:
Sub MyFoo()
Dim txtTest As TextBox
Set txtTest = frmFoo.txtExistingTextBox
End Sub
So I've come to the conclusion that somehow VBA tries to put the string value of frmFoo.txtExistingTextBox.Value in txtTest while I strictly want to get the TextBox control object.
I know that in some languages there is a way to define if you want or not do an affectation by reference instead of value, which would solve the problem here, but I have no idea how.
I know I could simply declare txtTest as Variant but I find this very unclean :-/
Thanks!
Your conclusion is wrong I'm afraid. The mismatch occurs because Excel's object library has a Textbox object, which is not the same as the MSForms textbox used on a userform. You should be using:
Sub MyFoo()
Dim txtTest As MSForms.TextBox
Set txtTest = frmFoo.txtExistingTextBox
End Sub
Set is always by reference. Try to declare the full type:
Private Sub UserForm_Click()
Dim control As MSForms.TextBox
Set control = Me.TextBox1
End Sub
It could also be a compilation issue. In that case, try to modify the module or rename it and run the code again.

Excel VBA Error 13 type mismatch in Me.ActiveControl

I'm creating a commmon validation subroutine for a form with multiple text boxes.
For each of the textbox I have an Exit event that triggers the following:
Private Sub formatoNumerico(Optional ByVal Cancel As MSForms.ReturnBoolean)
Dim tb As MSForms.TextBox
Set tb = Me.ActiveControl
' Do something
This is generating an Error 13 Type Mismatch on the 3rd line of the code, why?
Thanks in advance for helping!
The tab host control is a container so it takes precedence over its constituents.
The simplest thing is to pass the textbox to formatoNumerico otherwise you will need to identify the textbox by asking the current tab for its ActiveControl:
Private Sub formatoNumerico(Optional ByVal Cancel As MSForms.ReturnBoolean)
Dim tb As MSForms.TextBox
If Not (Me.ActiveControl Is Nothing) Then
If TypeOf Me.ActiveControl Is MultiPage Then
Set tb = Me.ActiveControl.Pages(Me.ActiveControl.Value).ActiveControl
Else
Set tb = Me.ActiveControl
End If
Debug.Print tb.Name
End If
End Sub
Also note that switching between controls on different tabs wont raise _Exit.
When you exit, the Textbox is no longer the active control, hence, when you try to set a textbox type variable to the current control (which may or may not be the textbox), this error will result.
You will need to modify your code to explicitly refer to the TextBox control you are interested in modifying. You may, perhaps, refer to it using some global variable at the time of initializing your forms and then use that reference in your code.