Search code examples
vbavb6userform

Difference between declaring a userform as Object vs MSForms.Userform?


Just a question that I can't seem to find an answer on.

I'm programmatically creating a userform, and I've found if I declare my object as the type "MSForms.Userform" there seems to be no way to set the height and width, as these properties don't exist, and insideheight / insidewidth are read only properties.

What I found was if I declare it as the generic type "object", I can set the height and width properties and use it exactly as I want.

So, after I initialize the object, I checked the locals window and the difference seems to be:

  • When declared as type "object" it will initialize as an instance of type "UserForm1"
  • When declared as type "MSForms.Userform" it will initialize as an instance of type "UserForm"

So my question is, what is the difference in using the different declare statments?

Thanks!

EDIT: Added some sample code so you can see how the oject act's differently when declared differently.
(I can't get this code block to display properly - even with the language declared as basic)

Sub TestUserForm()  
'NOTE:  You need to add a reference to Microsoft Visual Basic
'       for Applications Extensibility 5.3  

'Declare variables  
    Dim oForm           As MSForms.UserForm  
    Dim oForm1          As Object  
    Dim oComp           As VBComponent  
    Dim oComp1          As VBComponent  

'Create new form objects in the VBA project programmatically  
Set oComp = Application.VBE.ActiveVBProject.VBComponents.Add(ComponentType:=vbext_ct_MSForm)  
Set oComp1 = Application.VBE.ActiveVBProject.VBComponents.Add(ComponentType:=vbext_ct_MSForm)  

'Initailize an object of each new form  
    Set oForm = VBA.UserForms.Add(oComp.Name)  
    Set oForm1 = VBA.UserForms.Add(oComp1.Name)  

'Compare what happends when trying to set the width and height properties  
    With oForm1     'This works  
        .Height = 200  
        .Width = 100  
    End With  

    With oForm1     'This does not work  
        .Properties("Width") = 100  
        .Properties("Height") = 200  
    End With  

    With oForm      'This does not work  
        .Height = 200  
        .Width = 100  
    End With  

    With oForm      'This does not work  
        .Properties("Width") = 100  
        .Properties("Height") = 200  
    End With  

'Remove the forms from the project  
    Application.VBE.ActiveVBProject.VBComponents.Remove oComp  
    Application.VBE.ActiveVBProject.VBComponents.Remove oComp1  
End Sub  

Solution

  • When you import the components into the project it will name it UserForm1 and probably UserForm2 respectively.

    oForm == UserForm1

    oForm1 == UserForm2

    Now, looking at the MSDN docs for Object we find that:

    You can declare an object variable with the Object data type when the specific object type is not known until the procedure runs. Use the Object data type to create a generic reference to any object.

    You've declared the variables like so:

    Dim oForm           As MSForms.UserForm  
    Dim oForm1          As Object  
    

    So what happens when you initialize the objects is oForm gets initialized as a UserForm, while the runtime determines that the Object oForm1 is an instance of UserForm1, which is not the same thing.

    Try changing the component name of oForm1 prior to initializing it and you should pretty quickly see the difference.

    Now, if you want the type safety of declaring as a generic form and you want to access the Width property, you can cast your UserForm as an Object and access it like so.

    Dim FormAsForm As UserForm
    Dim FormAsObject As Object
    
    Set FormAsForm = New UserForm1
    Set FormAsObject = FormAsForm
    
    FormAsObject.Width = 200
    
    Debug.Print TypeName(FormAsForm)
    Debug.Print TypeName(FormAsObject)
    

    This is a trick we use often when implementing multiple interfaces. The compiler will only allow you to use properties that are defined in the particular type the class object is declared as.


    So what's the difference? Practically speaking, you get no intellisense when declaring things as Object. You also get no type safety. It's perfectly valid to do this (although not recommended.)

    Dim foo As New Baz
    Dim bar As New Qux
    Dim var As Object
    
    Set var = foo
    Set var = bar
    

    Object does come in extremely handy when you're using late binding to avoid adding references to your project though. Without the adding a reference, you're forced into using an unknown type.

    Dim xl As Object
    Set xl = CreateObject("Excel.Application")
    

    The other big difference is you're leaving it up to the runtime to determine what kind of object the variable will be. As you discovered, it will sometimes (rarely, but sometimes) produce surprising results.