vba 将用户表单声明为 Object 与 MSForms.Userform 之间的区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26408483/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference between declaring a userform as Object vs MSForms.Userform?
提问by CBRF23
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.
我正在以编程方式创建一个用户表单,我发现如果我将对象声明为“MSForms.Userform”类型,似乎无法设置高度和宽度,因为这些属性不存在,而 insideheight / insidewidth 是只读属性。
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:
因此,在初始化对象后,我检查了 locals 窗口,差异似乎是:
- 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"
- 当声明为“object”类型时,它将初始化为“UserForm1”类型的实例
- 当声明为“MSForms.Userform”类型时,它将初始化为“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
回答by RubberDuck
When you import the components into the project it will name it UserForm1
and probablyUserForm2
respectively.
当您将组件导入项目时,它会分别命名它UserForm1
和可能UserForm2
。
oForm == UserForm1
oForm1 == UserForm2
oForm == UserForm1
oForm1 == 用户Form2
Now, looking at the MSDN docs for Objectwe 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.
当在过程运行之前不知道特定对象类型时,您可以使用 Object 数据类型声明一个对象变量。使用 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 ObjectoForm1
is an instance of UserForm1, which is not the same thing.
那么当你初始化对象时会发生什么oForm
被初始化为一个用户窗体,而运行时确定该对象oForm1
是一个 UserForm1 的实例,这不是一回事。
Try changing the component name of oForm1 prior to initializing it and you should pretty quickly see the difference.
在初始化之前尝试更改 oForm1 的组件名称,您应该很快就会看到不同之处。
Now, if you want the type safety of declaring as a generic form andyou want to access the Width
property, you can cast your UserForm as an Object and access it like so.
现在,如果您想要声明为通用表单的类型安全并且想要访问该Width
属性,您可以将您的 UserForm 转换为一个对象并像这样访问它。
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.)
那么有什么区别呢?实际上,在将事物声明为 Object 时,您不会获得智能感知。你也没有类型安全。这样做是完全有效的(虽然不推荐。)
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.
另一个很大的区别是您将它留给运行时来确定变量将是什么类型的对象。正如您所发现的,它有时(很少,但有时)会产生令人惊讶的结果。
回答by hymced
I think you had your answer about the difference:
我想你对区别有你的回答:
- one is a UserForm object of class UserForm1, wrapped in an Object object (Type Object/UserForm1) that can be set to anything else later in proc,
- and the other is a UserForm object of class UserForm2.
- 一个是 UserForm1 类的 UserForm 对象,包装在一个 Object 对象(类型 Object/UserForm1)中,该对象稍后可以在 proc 中设置为其他任何内容,
- 另一个是 UserForm2 类的 UserForm 对象。
But the compiler will only allow to use properties/method existing for the type the class object is declared as :
但是编译器只允许使用类对象声明为类型的现有属性/方法:
- Objects have .Width and .Height properties, so oForm1 will allow you to access these properties of the instantiated form of the VBE component UserForm2 (take a look HEREfor further understanding on this), but oForm won't.
- UserForms have a .Show/.Hide method, and so will have oForm to display the instantiated/loaded form of the VBE component UserForm1, but oForm1 won't.
- 对象具有 .Width 和 .Height 属性,因此 oForm1 将允许您访问 VBE 组件 UserForm2 的实例化表单的这些属性(请查看此处以进一步了解这一点),但 oForm 不会。
- UserForms 有一个 .Show/.Hide 方法,因此会有 oForm 来显示 VBE 组件 UserForm1 的实例化/加载的表单,但 oForm1 不会。
I have made some tests, and I just wanted to point at that I think the corrected procedure would rather be :
我做了一些测试,我只是想指出,我认为更正的程序应该是:
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)
declaring here FormAsForm as a UserForm1, instead of UserForm which cause this error on my side :
在这里将 FormAsForm 声明为 UserForm1,而不是在我这边导致此错误的 UserForm:
Runtime error '438': Object doesn't support this property or method
运行时错误“438”:对象不支持此属性或方法
However, using this, after FormAsForm has be loaded and displayed once with .Show, then Unloaded, FormAsForm is downcasted from Type UserForm11/UserForm11 to UserForm11/UserForm, and in cannot be shown again with .Show. Any supposed to work method will cause this error :
但是,使用此方法,在 FormAsForm 已加载并使用 .Show 显示一次后,然后卸载,FormAsForm 从类型 UserForm11/UserForm11 向下转换为 UserForm11/UserForm,并且无法再次使用 .Show 显示。任何应该工作的方法都会导致此错误:
Error '-2147418105': Automation error
Error '-2147418105': Erreur Automation : L'appelé (serveur [pas application serveur]) n'est pas disponible et a disparu ; aucune connexion n'est valide. L'appel a peut-être été exécuté.
错误“-2147418105”:自动化错误
错误“-2147418105”:错误自动化:L'appelé (server [pas application server]) n'est pas disponible et a disparu ; aucune connexion n'est valide。L'appel a peut-être été exécuté。
Of course, the variable types I just gave are read from the Locals VBE Window, since ?TypeName(FormAsForm) will only bring a :
当然,我刚刚给出的变量类型是从 Locals VBE 窗口中读取的,因为 ?TypeName(FormAsForm) 只会带来:
Runtime Error '13': Type Mismatch
运行时错误“13”:类型不匹配
For the record, ?TypeName(FormAsObject) returns UserForm after downcast, and .Show method cause the same 'Automation error'.
作为记录, ?TypeName(FormAsObject) 在向下转换后返回 UserForm,并且 .Show 方法导致相同的“自动化错误”。
This whole part, I can't explain...
这整个部分,我无法解释......
回答by Mark Burns
So, unless I miss my guess here, MSFORMS.UserForm is a subclass of something else (something similar to a VB6 Form class, perhaps?), and the other properties & methods that it does handle properly, when dimensioned as an Object, (.Hide, .Show, .Visible, to name ones I know of so far) are neither a part of the Userform class interface, nor appear to be DOCUMENTED ANY-DAMN-WHERE-AT-ALL (and I wouldn't know of those listed above except from reading online questions/comments about UserForms)! ...must derive from the base class from which UserForm inherits?
因此,除非我在这里错过了我的猜测,否则 MSFORMS.UserForm 是其他事物的子类(可能类似于 VB6 Form 类?),以及它确实正确处理的其他属性和方法,当维度为对象时,( .Hide、.Show、.Visible,以我目前所知的命名)既不是 Userform 类界面的一部分,也不是 DOCUMENTED ANY-DAMN-WHERE-AT-ALL(我不知道上面列出的那些,除了阅读关于用户表单的在线问题/评论)!...必须从 UserForm 继承的基类派生?
Too bad we can't see what that base class is, because then we might be able to find an actual CLUE about what other capabilities are available to us (who knows, there may be .Parent, .Properties or other fun things "out there" that nobody seems to know about).
太糟糕了,我们看不到那个基类是什么,因为那样我们也许能够找到关于我们可用的其他功能的实际线索(谁知道,可能有 .Parent、.Properties 或其他有趣的东西“出来”那里”似乎没有人知道)。