我在Excel 2010中使用了一个功能区,其中包含一个按钮:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="rx_onLoad">
<ribbon>
<tabs>
<tab id="TestTab" label="Test Tab" insertAfterMso="TabHome">
<group id="TestGroup" label="TestGroup">
<button id="TestButton" label="TestButton" size="normal" onAction="OnTestButton" tag="TestButton" imageMso="Coffee" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
OnTestButton方法在模块
中实现Sub OnTestButton(Control As IRibbonControl)
Dim Ws As Object
Set Ws = Control.Context.ActiveSheet
MsgBox ActiveSheet Is Ws ' Shows True
Debug.Print ActiveSheet.Name 'OK
Debug.Print Ws.Name ' OK
ActiveSheet.Test ' OK
Debug.Print Ws.Test ' Runtime Error
End Sub
活动表格有方法
Public Sub Test()
MsgBox "Test"
End Sub
如果单击测试按钮,则调用方法OnTestButton
。
对象Control.Context.ActiveSheet
和ActiveSheet
在IS
- 运算符下相同。当我使用Name
之类的属性时,它位于WorkSheet
的界面中,它们的行为相同。
但是,当我调用方法Test
时,不在界面中,我在Control.Context.ActiveSheet
上得到运行时错误458“对象不支持此属性或方法”,但ActiveSheet
上没有。
那么为什么这两个引用Control.Context.ActiveSheet
和ActiveSheet
在运行时它们应该引用“相同”对象时表现不同呢?
答案 0 :(得分:4)
我合理地(> 90%)确定以下内容是真的。
首先回答标题中的问题:VBA中两个对象何时相同?
当COM表示它们相同时,VBA中的两个对象是相同的,而COM表示当request the IUnknown
interface from both and the pointers come out equal时它们是相同的。
现在对相关对象。
功能区的Control.Context
只不过是Excel Application.ActiveWindow
,因此问题变为 ActiveWindow.ActiveSheet
和Application.ActiveSheet
相同。强>
是的,他们 - 就COM而言
它们可能没有在内部实现作为单个对象,因为它们的指针彼此相距很远,但是当您从它们请求IUnknown
时,它们会返回相同的指针值。 (您通过声明类型IUnknown
和IUnknown
对象的变量来请求Set
。)
附注。
Excel有多个&#34;外部&#34; &#34;实例&#34;对于单个&#34;实际&#34; &#34;内部&#34; &#34;真实对象的实例&#34;。
E.g。您可以创建Range
对象的多个实例,所有实例都是不同的实例(Is = False
),这些实例引用实际工作表上完全相同的实际范围。因此,每一个都只是一个视频&#34;对于&#34;实际的事情&#34;。
我推测Window
和Sheet
s也会发生类似的事情(可能有多个&#34;视口和#34;对于每个实际的东西),但Excel开发人员,为了避免混淆/简化VBA编码,决定让Sheet
包装器通过返回相同的IUnknown
指针报告它们是同一个对象。
就COM而言,这是好的:只要遵循所有COM规则,如果对象在内部实现为多个对象并不重要,因为只要遵循所有COM规则,就没有无论如何,告诉他们分开的方式。
现在是实际问题,为什么Test()
无法调用ActiveWindow.ActiveSheet
。
因为ActiveWindow.ActiveSheet
会返回Worksheet
接口,该接口没有Test()
方法且为non-extensible。就这么简单。
那么为什么Test()
可以调用Application.ActiveSheet
?
因为Application.ActiveSheet
未返回Worksheet
。它返回Sheet1
(或者您的工作表命名)。
Sheet1
是一个动态界面,继承自Worksheet
并包含您的Test()
。它是Worksheet
的超集。
您可能想知道为什么当用户尝试在Worksheet
上呼叫Test()
时,VBA不会请求更好的Worksheet
超集。答案是,既不应该,也不能!
VBA不应该也不需要了解托管它的应用程序的内部实现细节。它不可能知道存在更好的&#34;可以从当前界面查询的界面。如果它确实怀疑有一个更好的&#34;接口,考虑到每个对象可以有数百个接口,它究竟会尝试查询哪一个?
VBA唯一需要执行后期绑定调用的是IDispatch
接口
所有Excel界面都继承自IDispatch
。
也就是说,Excel中的每个类Is IDispatch
。
As Object
类型的变量也意味着As IDispatch
将某些内容设置为Object
变量意味着从该对象中查询IDispatch
。
ActiveWindow.ActiveSheet
会返回IDispatch
的{{1}}。它是有效的,完整的Worksheet
存储在IDispatch
类型的变量中,因此不需要VBA要求更好IDispatch
&# 34; 即可。它使用已有的那个,并且调用失败。
IDispatch
返回Application.ActiveSheet
,它是IDispatch
的一部分,是一个不同的界面。这次Sheet1
成功。
Test()
从ActiveWindow.ActiveSheet
返回IDispatch
是否是一个错误,这是否值得商榷。
从技术上讲,它不是一个错误,因为Worksheet
是 Worksheet
所以该方法有权返回。
然而,可以说回归更好IDispatch
&#34;我们是如何在Excel中滚动的,他们真的应该这样做
我个人倾向于宣称这是一个小错误。
但你可以要求更好IDispatch
&#34;你自己。
如果您只是声明另一个IDispatch
变量并且{/ 1}}您现有的工作表引用,那么它将无法工作 - VBA将观察到两个变量的类型相同,Object
和将直接复制指针而不是试图查询另一个接口。
要让VBA实际要求另一个界面,首先需要查询与Set
不同的其他界面,然后从中请求Object
:
IDispatch