将用户窗体声明为对象与MSForms.Userform的区别是什么?

10

我有一个问题,但是找不到答案。

我正在以编程方式创建用户表单,并发现如果将对象声明为类型“MSForms.Userform”,似乎没有办法设置高度和宽度,因为这些属性不存在,而insideheight / insidewidth是只读属性。

我发现,如果将对象声明为通用类型“object”,则可以设置高度和宽度属性并完全按照所需使用。

因此,在初始化对象后,我检查了本地窗口,两者之间的区别似乎是:

  • 当声明为类型“object”时,它将初始化为类型“UserForm1”的一个实例
  • 当声明为类型“MSForms.Userform”时,它将初始化为类型“UserForm”的一个实例

因此,我的问题是,使用不同的声明语句有什么区别?

谢谢!

编辑:添加了一些示例代码,以便您可以看到在不同声明时该对象的行为不同。
(即使声明了基本语言,我也无法正确显示此代码块)

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  

3
UserForm1UserForm的一个特定实例,仅存在于您的VBA项目中。类似于Sheet1Worksheet - Tim Williams
Tim,我觉得我明白你的意思了,但是对于声明对象类型的区别仍然感到困惑。我在原来的问题中添加了一些示例代码,这样你就可以看到我在说什么了。 - CBRF23
1
至少在某种程度上,区别在于当您将其声明为对象时,VB不会尝试验证类型。您无法获得智能感知,并且正如您所看到的,它可以改变您访问属性的方式。当您声明为某种特定类型的内容时,VB将获取有关该类型的信息,并在智能感知中提供属性和方法。它还将防止您将oForm设置为不是UserForm的内容。 - jac
1
我不是真正的OO(面向对象)程序员,但我想把一个特定的用户表单分配给一个声明为UserForm的变量类似于将一个类“向上转型”(向下转型?)到其基类。如果您使用“Locals”窗口比较oFormoForm1的属性,您会发现(例如)oForm没有Width属性,而oForm1有。我以前从未使用过“Properties ()”语法,并且在这种情况下似乎不适用。 - Tim Williams
3个回答

13
当您将组件导入项目时,它将分别命名为 UserForm1可能是 UserForm2

oForm == UserForm1

oForm1 == UserForm2

现在,查看 Object 的 MSDN 文档,我们发现:

当特定的对象类型直到过程运行时才知道时,可以使用 Object 数据类型声明对象变量。使用 Object 数据类型创建对任何对象的通用引用。

您已经声明了变量如下:
Dim oForm           As MSForms.UserForm  
Dim oForm1          As Object  

那么当您初始化对象时会发生什么呢?oForm被初始化为一个UserForm,而运行时确定oForm1对象实例是UserForm1的一个实例,这并不相同。

尝试在初始化之前更改oForm1的组件名称,您应该很快就能看到差异。

现在,如果您想要声明为通用表单并且要访问Width属性的类型安全性,可以将UserForm强制转换为Object并像这样访问它。

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)

在实现多个接口时,我们经常使用的一个技巧是:编译器只允许您使用在类对象声明时定义的属性。


那么有什么区别呢?从实际角度来看,当将事物声明为Object时,您将无法获得智能感知。此外,您也无法获得类型安全性。尽管不建议这样做,但完全可以这样做。

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

Set var = foo
Set var = bar

当你使用晚期绑定以避免向项目添加引用时,Object会非常方便。如果不添加引用,你会被迫使用未知类型。

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

另一个重要的区别是你让运行时决定变量将成为什么类型的对象。就像你发现的那样,它有时会产生令人惊讶的结果(虽然很少,但有时会出现)。


1
ckuhn2003,非常感谢您对两种情况发生的详细解释。这对我理解正在发生的事情非常有帮助。所提供的示例易于跟踪和理解。谢谢! - CBRF23

1
我认为你已经了解了差异:
  • 一个是 UserForm1 类的 UserForm 对象,包装在一个 Object 对象中(类型为 Object/UserForm1),稍后可以将其设置为其他任何内容的 proc,
  • 另一个是 UserForm2 类的 UserForm 对象。

但编译器只允许使用类对象声明的类型存在的属性/方法:

  • 对象具有 .Width 和 .Height 属性,因此 oForm1 将允许您访问 VBE 组件 UserForm2 的实例化表单的这些属性(请参阅 HERE 以进一步了解),但 oForm 不会。
  • UserForms 具有 .Show/.Hide 方法,因此 oForm 将显示 VBE 组件 UserForm1 的实例化/加载表单,但 oForm1 不会。

我进行了一些测试,我想指出我认为更正的过程应该是:

Dim FormAsForm As **UserForm1***
Dim FormAsObject As Object

Set FormAsForm = New UserForm1
Set FormAsObject = FormAsForm

FormAsObject.Width = 200

Debug.Print TypeName(FormAsForm)
Debug.Print TypeName(FormAsObject)

在这里将FormAsForm声明为UserForm1,而不是UserForm,这会导致我这边出现以下错误:

运行时错误'438': 对象不支持此属性或方法


然而,使用这种方式,在FormAsForm已通过.Show加载和显示一次后,它就从Type UserForm11 / UserForm11降级为UserForm11 / UserForm,并且不能再次使用.Show显示。任何预期工作的方法都会导致以下错误:

错误'-2147418105': 自动化错误

错误'-2147418105': Automation Error: 被调用的服务(非应用程序服务器)不可用并且已消失;没有有效连接。可能已执行调用。


当然,我刚给出的变量类型是从Locals VBE窗口中读取的,因为?TypeName(FormAsForm)只会带来一个:

运行时错误'13': 类型不匹配


值得一提的是,? TypeName(FormAsObject)在降级后返回UserForm,并且.Show方法会导致相同的“自动化错误”。

empty

整个部分,我无法解释...


1
所以,除非我猜错了,MSFORMS.UserForm是其他类的子类(可能类似于VB6 Form类?),而当它被定义为对象时,它正确处理的其他属性和方法(例如.Hide、.Show、.Visible等)既不是Userform类接口的一部分,也似乎没有在任何地方记录(我只知道上面列出的这些是从在线问题/评论中读到的)!...必须从UserForm继承的基类派生?太糟糕了,我们无法看到那个基类是什么,因为这样我们可能能找到有关我们可用的其他功能的实际线索(谁知道,可能还有.Parent、.Properties或其他有趣的东西“存在”,但似乎没有人知道)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接