Question

I have a simple user form with a button on it. I want to pass this form to a function in another module - but even inside the same module, it does not work as expected:

Private Sub Test(ByRef oForm As MSForms.UserForm)
    Debug.Print "caption: >" & oForm.Caption & "<"
End Sub

Private Sub CommandButton3_Click()
    Debug.Print "caption: >" & Me.Caption & "<"
    Test Me
    Debug.Print "caption: >" & Me.Caption & "<"
End Sub

when I click the button it prints this to the debug console:

caption: >UserForm1<
caption: ><
caption: >UserForm1<

so inside of the Test() sub the Caption is blank.

any ideas why this does not work?

Note: if I use Variant as parameter-type it works

Was it helpful?

Solution

Type UserForm is not directly equivalent to an instance of a specific User Form and presents a slightly different interface.

Pass the form as Object:

Private Sub Test(Form As Object)

or for a type safe approach a UserForm may implement an Interface:

Private Sub Test(Form As IMyForm)

OTHER TIPS

What worked for me was the following...

1) Define a global parameter in the main module as follows:

Public gMyForm As MSForms.UserForm

2) In the UserForm_Initialize() sub:

Set gMyForm = Me

3) To call the function:

Dim l_dummy as Variant
l_dummy = myFunction(gMyForm)

4) Function signature plus a few lines of random code:

Function myFunction(ByRef p_form As MSForms.UserForm) 

Dim l_my_value type string
l_my_value = p_form.myControl.Value  

The only weirdness, which doubtless someone can explain, is the need to specify the l_dummy return parameter else you get the type mismatch error. Worked like a charm otherwise..

Hope this helps someone.

You have to pass a variable of your UserForm's type. It means that if your form's name is MyForm then the signature shoud be

Private Sub Test(ByRef oForm As MyForm)

"The only weirdness, which doubtless someone can explain, is the need to specify the l_dummy return parameter else you get the type mismatch error. Worked like a charm otherwise.. Hope this helps someone."

These are features of the language syntax. You use the function, but do not specify the type of return value. If you don't need to return anything then use Sub(). Brackets must be specified if the function returns something :

variant ret = myFunction(gMyForm)

if it doesn't return:

myFunction gMyForm

to avoid creating unnecessary variables: not return: myFunction Me return:... = myFunction(Me) Signature Method/Function:

Sub myFunction(p_form As MyForm)
........
End Sub

Or

Function myFunction(p_form As MyForm) As Variant
............
   myFunction = p_form.myControl.Value
End Function

Below is the solution I found. The important parts are:

  1. At the Call site into the Sub, the Call is required IF THE ARGUMENT IS WITHIN PARENTHESIS. When I left out Call and kept the parenthesis around the argument, I received an error of Run-time error '438': Object doesn't support this property or method. And when selecting "Debug" in the error dialog, I am dropped into the VBA editor and it indicates the .Visible reference within the Sub itself is what was not found. The error disappears when the parenthesis around the argument is removed.
  2. Within the Sub, the parameter accepting the form, pv_objFrmTarget, must be defined as ByVal. It explicitly cannot be defined as ByRef or left undefined (which defaults to ByRef). When I used ByRef or left it out, I received an error of Run-time error '13': Type Mismatch. And when selecting "Debug" in the error dialog, I am dropped into the VBA editor and it indicates the Call s_... statement is in error (as in it is attempting to update the ByRef value following the completion of the call).
  3. Within the Sub, the type of the parameter accepting the form, pv_objFrmTarget, must be defined as either As Object or As Variant or not defined at all (which I believe results in defaulting to As Variant). Defining it as 'UserForm' results in an error of Run-time error '438': Object doesn't support this property or method. And when selecting "Debug" in the error dialog, I am dropped into the VBA editor and it indicates the .Visible reference within the Sub itself is what was not found.

Solution 1:

Code at Call Site:

Sub btnClear_Click()
  Call s_FrmShowCenteredOnlyIfHidden(frmClear)
End Sub

Code at Sub Site:

Public Sub s_FrmShowCenteredOnlyIfHidden(ByVal pv_objFrmTarget As Object)
  With pv_objFrmTarget
    If (.Visible = False) Then
      .StartUpPosition = 0
      .Left = Application.Left + (0.5 * Application.Width) - (0.5 * .Width)
      .Top = Application.Top + (0.5 * Application.Height) - (0.5 * .Height)
      .Show
    End If
  End With
End Sub

Solution 2: [all defaults]

Code at Call Site:

Sub btnClear_Click()
  s_FrmShowCenteredOnlyIfHidden frmClear
End Sub

Code at Sub Site:

Public Sub s_FrmShowCenteredOnlyIfHidden(ByVal pv_objFrmTarget)
  With pv_objFrmTarget
    If (.Visible = False) Then
      .StartUpPosition = 0
      .Left = Application.Left + (0.5 * Application.Width) - (0.5 * .Width)
      .Top = Application.Top + (0.5 * Application.Height) - (0.5 * .Height)
      .Show
    End If
  End With
End Sub

Pass the userform as Object, like here:

..
Call Subroutine(Me)
..

Sub Subroutine(Dialog As Object)
    Dim UF As Object
    Set UF = Dialog
    Debug.Print Dialog.caption, UF.caption
End Sub
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top