本帖最后由 jiulongpo 于 2012-8-6 20:26 编辑
转自: http://www.excelperfect.com/index.php/2009/07/18/ribbonxstudy15/
细品RibbonX(15):回调(CallBacks)
回调,即使自定义界面能够工作的代码。如果没有回调,那么自定义界面可能看起来漂亮,但只是一个漂亮的功能区。当然,除内置控件不需要自定义回调外。 下面的内容介绍如何使用回调来提供自定义控件所需的功能。在编写XML代码时,需要指定回调,然后在Excel中编写VBA代码来匹配和处理回调。 回调概要 回调是自定义的用户界面使用的子过程和函数,使自定义的用户界面能够工作。回调简单地表现为所提供的指令的动向。例如,当设置某按钮的onAction属性并装载用户界面时,一旦单击该按钮,就产生回调,即在属性中指定的操作。 如果没有找到指定的操作,那么回调将失败,因为代码中发生了例外,即在属性中指定的回调在VBA中不存在,因此失败了。 自定义用户界面一装载就会调用VBA工程,搜寻指定的函数或过程。如果找到,则将其值传回到用户界面。如果没有找到指定的函数或过程,那么将产生错误的结果。 有两种主要的方式来创建回调: n 直接在VBE的标准模块中输入过程或函数 n 使用工具生成过程,例如Office 2007 CustomUI Editor 使用诸如Office 2007 CustomUI Editor这类工具的主要优势是能够扫视XML代码并返回回调签名,也称作subprocedure stub,XML代码中每个属性都有一个需要处理的回调。接受回调作为值的属性包括onAction、getVisible和getImage。 除了节省时间外,使用CustomUI Editor还能避免由于手工编写回调签名而导致的拼写错误。上述每个属性都会产生一个不同的回调签名,必须按顺序使用代码处理,使用户界面有合适的功能。 此外,当工程装载时需要访问某些回调并运行,这意味着如果回调处理(即响应回调的VBA代码)在该工程中不存在,将产生错误消息。当然,下面的内容也会介绍如何缓解这类错误,甚至如何避免错误。 为动态回调建立文件 为了使自定义控件工作,文件必须启用宏,否则不能够添加或运行VBA代码。 1、捕获IRibbonUI对象 IRibbonUI对象引用功能区用户界面,用于控制事物如何响应。 在VBA中使用的关键之一是使整个Ribbon对象无效(因而能够改变功能区的某项特征)或者使功能区中指定的控件无效(因而能够改变该控件的某项特征)。 (1)调整XML以包括onLoad 为了使用IRibbonUI对象,需要在VBA中对其设置。首先,需要对UI的onLoad属性指定值,这能通过为onLoad属性指定回调来实现: <customUI xmlns=“http://schemas.microsoft.com/office/2006/01/customui“ onLoad=“rxIRibbonUI_onLoad“> 因为onLoad属性的值是一个回调,所以需要在VBA代码中对其进行处理。接着,能够在工程中使用IRibbonUI对象。 (2)创建VBA代码处理onLoad事件 可能会注意到,前面将onLoad称作为属性,但现在将其称为事件。这是因为在XML文件中,能为许多属性定义值,例如onAction、getLabel和onLoad。一旦为某属性赋值,如果能以某种方式触发,将导致事件发生。这就是属性和事件的区别。 因为IRibbonUI对象用于整个应用程序,所以需要在标准模块的全局声明部分对其声明,如下面的代码所示。 ‘全局的ribbon对象 Dim grxIRibbonUI As IRibbonUI 这里使用标准的格式grxIRibbonUI引用IRibbonUI对象,接着添加回调来设置该对象: Sub rxIRibbonUI_onLoad(ribbon As IRibbonUI) Set grxIRibbonUI = ribbon End Sub 设置为全局对象之后,该对象可用于整个工程。然而,Ribbon对象对于修改是非常敏感的。这意味着任何时候修改代码,该对象实例将丢失并且任何需要Ribbon对象的操作都将失败。因此,无论何时作出修改,都需要保存、关闭并重新打开工程。 生成第一个回调 知道需要获得回调签名后,还需要知道如何使用回调签名。例如,对于切换按钮(toggleButton)有下列签名: Sub rxtgl_click(control as IRibbonControl,pressed as boolean) 一个普通按钮的签名如下: Sub rxbtn_click(control as IRibbonControl) 1、从头开始编写回调 如果知道了回调签名,则不必使用标准的形式声明参数。例如,有一个使用下列XML的切换按钮: <toggleButton id=”rxtgl” label=”Toggle” size=”large” onAction=”rxtgl_click” imageMso=”FormatPainter”/> 编写rxtgl_click回调如下: Sub rxtgl_click(rxctl As IRibbonControl,toggled As Boolean) If toggled Then MsgBox “我已切换 …我的ID是 “ & rxctl.ID,vbInformation End If End Sub 注意到回调的参数名与上面介绍的标准的参数名不一样,但是单击切换按钮时仍触发该回调,像平常一样传递参数,因此签名中的修改不会导致过程失败,只要使用了正确类型的正确参数。 也能修改onLoad回调签名来满足需要,如下所示: Sub rxIRibbonUI_onLoad(MyRibbon As IRibbonUI) Set grxIRibbonUI=MyRibbon End Sub 然而,如果使用标准的签名及其参数名称,那么其他人将更容易理解代码。下面列出了一些回调签名。 属性onLoad,回调签名(ribbon as IRibbonUI); 属性getLabel,getPressed,getEnabled,getImage,getScreentip,getVisible,等,回调签名(control as IRibbonControl,ByRef returnedVal); 属性onAction(切换按钮),回调签名(control as IRibbonControl,pressed as Boolean); 属性onAction(按钮),回调签名(control as IRibbonControl)。 2、使用Office CustomUI Editor生成回调 一种很容易生成回调的方法是使用CustomUI Editor。这个工具将所有的返回回调的属性搜寻在一起,然后生成需要的回调。因此,无须追踪XML中所有的回调。对于大段的XML代码来说,这是无价的。 在CustomUI Editor中(以Excel为例),可以使用下列步骤自动生成必需的回调: 步骤1 使用CustomUI Editor打开包含XML代码的Excel。 步骤2 单击“Generate Callbacks”按钮。 步骤3 出现一个新的Callbacks选项卡。复制产生的代码并粘贴到VBA工程中。 注意,确保验证代码。 3、理解文件打开时事件的顺序 在工程中添加了回调后,该文档打开时将调用某些过程,具体调用的过程取决于工程打开时定制是否获得了焦点。注意,某些过程仅当包含定制的选项卡获取焦点时才调用,而其它过程当鼠标移动到控件之上时调用。 理解过程调用的顺序是复杂的,因为调用顺序受许多可变因素影响。为了帮助预测典型的事件顺序,下表列出了功能区选项卡的事件及相应的顺序。 表:当打开工程后选项卡获取焦点时的事件顺序 事件
| 选项卡获取焦点
| 选项卡有焦点
| 按下ALT键
| 鼠标在其上方
| onLoad
| 最顶级的事件,当装载UI时发生
| getVisible
| 1
| 1
| N/A
| N/A
| getLabel
| 2
| 3
| N/A
| N/A
| getImage
| 3
| 4
| N/A
| N/A
| getEnabled
| 4
| 2
| N/A
| N/A
| getKeytip
| N/A
| N/A
| 1
| N/A
| getScreentip
| N/A
| N/A
| N/A
| 1
| getSupertip
| N/A
| N/A
| N/A
| 2
|
上表仅列出了可以使用的通用属性的一些示例,然而顺序可能受其它引入的属性的影响,例如getDescription、getTitle,等等。虽然如此,打算在性能方面以最好的方式解决用户界面问题时,上表仍然可以作为一个通用的向导。 4、能够有具有相同名称但不同签名的两个回调吗? VBA不允许在相同工程中使用相同名称不同签名的两个回调。然而,如果回调在不同的工程中,则相同的回调名称能够有不同的签名。因此,如果同时打开一个以上的Excel文档,那么可能发现回调返回不可预料的结果。因为如果多个操作(签名)与一个回调名称相联系,那么将运行当前文档的回调。 假设打开一个安装了两个加载项的Excel工作簿,三个项目都有一个带有称为rxbtnnsQaShared的控件的用户界面。当单击用户界面中该控件时,所期望的是添加一个新工作簿,然而将出现一个消息框。检查后,发现为所有三个按钮都使用了相同的回调。此时,可以使用断点调试,看看单击操作调用了哪个回调。 上述表明,只要在不同工程中,虽然允许使用带有不同签名相同名称的回调,但这不是一个好主意。 调用位于不同工作簿中的过程 与上面所讲的情况相似,如果XML代码运行位于不同工作簿中的VBA,也会碰到类似的问题。 假设有两个工作簿:Book.xlsm和Book2.xlsm,希望在第一个工作簿中添加一个按钮,运行第二个工作簿中的过程。使用下面的XML代码创建用户界面: - <customUI xmlns=“http://schemas.microsoft.com/office/2006/01/customui“>
- <ribbon>
- <tabs>
- <tab id=“rxtabDemo“
- label=“My Custom Tab“
- insertBeforeMso=“TabHome“>
- <group id=“rxgrpDemo“
- label=“My Demo Group“>
- <button id=“rxbtnDemo“
- label=“My Demo Button“
- size=“large“
- onAction=“Book2.xlsm!rxbtnDemo_click“
- imageMso=“FileStartWorkflow“/>
- </group>
- </tab>
- </tabs>
- </ribbon>
- </customUI>
复制代码
在Book2.xlsm的标准模块中,放置下列代码:
Sub rxbtnDemo_click(control As IRibbonControl) MsgBox “您调用了位于:” & ThisWorkbook.Name & “中的过程.” End Sub 注意,必须要打开工作簿才能运行。如果希望onAction属性指向已装载的加载项而不是工作簿,那么简单地使用[add-in name].xlam在onAction代码名称前加上前缀,例如: onAction=”myAddIn.xlam!rxbtnDemo_click” 然后,在加载项的标准模块中放置回调VBA代码: Book3.xlam!rxbtnDemo_click 此时,必须加上前缀xlam!。注意,如果在用户界面的onAction属性下指定的过程中xlsm和xlam有相同的名称,那么该事件会运行包含在活动工作簿中的代码。你可能已注意到,在前面的介绍的内容中我们必须移除xlsm!,但使用加载项时必须有xlam!。 组织回调 随着功能区自定义代码技术的不断熟练,你将会采取不同的方式来组织回调,可以有单个的回调处理,或者有全局的回调处理,一次处理多个控件。 1、单个的回调处理 编写XML代码时,可以指定返回回调的多种属性,例如onAction、getLabel、getVisible、getEnabled,等等。这些属性的每一个都必须处理。下面以Excel为例,通过生成组中的3个按钮来演示这个过程。 - <customUI xmlns=“http://schemas.microsoft.com/office/2006/01/customui“>
- <ribbon>
- <tabs>
- <tab id=“rxtabDemo“
- label=“My Custom Tab“
- insertBeforeMso=“TabHome“>
- <group id=“rxgrpDemo“
- label=“My Demo Group“>
- <button id=“rxbtnPaste“
- label=“My Paste Button“
- size=“normal“
- onAction=“rxbtnPaste_click“
- imageMso=“Paste“
- tag=“Custom Paste Button“/>
- <button id=“rxbtnCopy“
- label=“My Copy Button“
- size=“normal“
- onAction=“rxbtnCopy_click“
- imageMso=“Copy“
- tag=“Custom Copy Button“/>
- <button id=“rxbtnCut“
- label=“My Cut Button“
- size=“normal“
- onAction=“rxbtnCut_click“
- imageMso=“Cut“
- tag=“Custom Cut Button“/>
- </group>
- </tab>
- </tabs>
- </ribbon>
- </customUI>
复制代码每个按钮都有其onAction属性,因此如果企图对按钮添加功能,则必须处理每个属性: Sub rxbtnPaste_click(control As IRibbonControl) MsgBox “您单击了” & control.Tag, vbInformation End Sub Sub rxbtnCopy_click(control As IRibbonControl) MsgBox “您单击了” & control.Tag, vbInformation End Sub Sub rxbtnCut_click(control As IRibbonControl) MsgBox “您单击了” & control.Tag, vbInformation End Sub 单独处理每个属性非常繁锁且难以维护,而一种可供选择的方法是一次处理多个属性。
|