【问题标题】:Microsoft Word: How can I listen to a document-level event (protect/unprotect) to update a ribbon toggle button?Microsoft Word:如何收听文档级事件(保护/取消保护)以更新功能区切换按钮?
【发布时间】:2014-04-16 21:56:53
【问题描述】:

PHB 希望我为 Microsoft Word 创建一个功能区切换按钮:

  • 按下时,将编辑限制为填写表格并在没有密码的情况下保护文档。
  • 未按下时,取消对文档的保护(无需密码)。

我有以下 customUI.xml:

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="RibbonOnLoad">
    <commands>
        <command idMso="ProtectOrUnprotectDocument" onAction="ProtectOrUnprotectDocumentOnAction"/>
    </commands>
    <ribbon>
        <tabs>
            <tab id="LawTab" label="Law">
                <group id="ProtectGroup" label="Protect">
                    <toggleButton id="ToggleProtectionButton" imageMso="GreenBall" label="Protection" getPressed="ToggleProtectionButtonGetPressed" onAction="ToggleProtectionButtonOnAction"/>
                    <button id="InvalidateRibbonButton" imageMso="Refresh" label="Invalidate Ribbon" onAction="InvalidateRibbonButtonOnAction"/>
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

以及以下 VBA 代码:

Private ribbon As IRibbonUI

Sub InvalidateRibbonButtonOnAction(control As IRibbonControl)
    ribbon.Invalidate
End Sub

Sub ProtectOrUnprotectDocumentOnAction(control As IRibbonControl, ByRef cancelDefault)
    ribbon.Invalidate
    cancelDefault = False
End Sub

Sub RibbonOnLoad(ActiveRibbon As IRibbonUI)
    Set ribbon = ActiveRibbon
End Sub

Sub ToggleProtectionButtonGetPressed(control As IRibbonControl, ByRef returnValue)
    returnValue = ActiveDocument.ProtectionType <> wdNoProtection
End Sub

Sub ToggleProtectionButtonOnAction(control As IRibbonControl, ByVal pressed As Boolean)
    If pressed Then
        ActiveDocument.Protect wdAllowOnlyFormFields
    Else
        ActiveDocument.Unprotect
    End If
End Sub

我可以重新调整内置 ProtectOrUnprotect 命令的用途,以便相应的内置按钮使功能区无效并因此更新我的自定义按钮,但如果我使用内置任务窗格(查看 > 限制编辑)或以编程方式保护/取消保护(ActiveDocument.Protect/Unprotect),我的自定义按钮对更改一无所知。如何监听文档级事件以保护/取消保护,以便更新切换按钮的状态?

【问题讨论】:

  • +1 提出一个好问题。用于功能区操作的资源并不多。如果我有几分钟的空闲时间,我会看看我是否能弄清楚......
  • 请在ToggleProtectionButtonGetPressed函数中插入一个消息框。您是否可以通过 Word 中的用户操作调用此功能?我不能这样做。如果你是,请告诉我你是怎么做的,我会继续努力的。否则,您的解决方案可能需要类似于 PowerPoint (here) 所需的加载项,如果甚至可以响应此特定事件(可能不是)...
  • 我创建了一个无效功能区按钮。要对其进行测试,请使用内置任务窗格(查看 > 限制编辑)来启动/停止保护。在您按下“无效功能区”按钮之前,我的自定义按钮将忽略实际的保护状态。

标签: vba ms-word ribbon ribbonx


【解决方案1】:

forcing the built-in button to call your code 怎么样?

总而言之,您可以通过将以下内容添加到功能区 XML(有关详细信息,请参阅链接)来覆盖内置的工作表保护按钮以使用您的代码(希望这会导致您的按钮切换):

<commands>
    <command idMso="ProtectOrUnprotectDocument" onAction="ToggleProtectionButtonOnAction"/>
</commands>

【讨论】:

  • 重新利用 ProtectOrUnprotectDocument 命令有效。 . .但仅适用于该命令。如果我使用内置任务窗格(查看 > 限制编辑)或以编程方式(ActiveDocument.Protect/Unprotect)保护/取消保护,我的自定义按钮将不知道更改。
  • @Homer 和 David,感谢您的提醒,我想我通过查看我链接的 Excel 示例让自己感到困惑。为了后代,我正在更新答案以使用“ProtectOrUnprotectDocument”ID。
  • @Homer 为“ReviewRestrictFormatting”添加一个额外的命令行怎么样? This page 似乎有一个非常完整的按钮 ID 列表。我看不到任何可以在 VBA 中直接使用的事件:'(
  • 这仍然错过了以编程方式保护/取消保护(ActiveDocument.Protect/Unprotect)。我可以将观察者模式拼凑在一起,并告诉每个人始终使用一个在幕后调用内置 Protect/Unprotect 的层,并且永远不要直接使用内置的 Protect/Unprotect 。 . .但没有。
  • @Homer "...但是没有" - 同意:P
【解决方案2】:

我认为 Blackhawk 的答案是正确的:覆盖内置命令的 onAction 过程。但是,他的 XML 示例适用于 MS Excel,不适用于 MS Word。这可以很容易地调整,但不幸的是,我不知道如何解决这个特殊问题:

但是如果我使用内置的 ProtectOrUnprotectDocument 按钮来保护/取消保护,我的自定义按钮将不知道更改。如何监听文档级事件以保护/取消保护,以便更新切换按钮的状态?

没有文档级别的事件,即使使用WithEvents 应用程序类,它响应文档的ProtectionType 的变化(理论上你应该可以使用功能区.InvalidateControl 方法)。

所以问题(和可能的解决方案)是为什么您需要一个切换按钮,当您可以简单地使用内置按钮并通过您自己的程序劫持它的功能来根据需要保护/取消保护时.您甚至可以将内置按钮放置在自定义菜单中。

不过,这看起来很有希望:

http://sourcedaddy.com/ms-excel/getting-and-changing-control-values.html

修订

经过一些讨论和反复试验(你自己做了很多这样的事情,并弄清楚了我做不到的事情),让我们试试这个对我有用的东西。

这里是 XML(如果您使用的是旧版本,则可能需要修改架构)。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <customUI onLoad="RibbonOnLoad" xmlns="http://schemas.microsoft.com/office/2009/07/customui">
    <commands>
        <command idMso="ProtectOrUnprotectDocument" onAction="ToggleProtectionButtonOnAction" />
    </commands>
    <ribbon>
        <tabs>
            <tab id="LawTab" label="Law">
                <group id="ProtectGroup" label="Protect">
                    <toggleButton id="ToggleProtectionButton" imageMso="GreenBall" label="Protection" getPressed="ToggleProtectionButtonGetPressed" onAction="ToggleProtectionButtonOnAction"/>
                    <button id="InvalidateButton" imageMso="Refresh" label="Invalidate" onAction="InvalidateButtonOnAction"/>
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

我所做的是将bothProtectOrUnprotectDocument 命令您的自定义切换按钮ToggleProtectionButton 发送到同一个onAction 处理程序。然后我使用一些逻辑(似乎有效)选择性地使功能区无效,而布尔变量在整个过程和回调中捕获文档的保护状态。

这里是 VBA:

Option Explicit
Dim ribbon As IRibbonUI
Dim protectButton As IRibbonControl
Dim IsProtected As Boolean

Sub InvalidateRibbonButtonOnAction(control As IRibbonControl)
'MsgBox "Invalidate"
    ribbon.Invalidate
End Sub

Sub RibbonOnLoad(ActiveRibbon As IRibbonUI)
'MsgBox "onLoad"
    Set ribbon = ActiveRibbon
    IsThisDocumentProtected
End Sub

Sub ToggleProtectionButtonGetPressed(control As IRibbonControl, ByRef returnValue)
'MsgBox "GetPressed"
    IsThisDocumentProtected
    returnValue = IsProtected
End Sub

Sub ToggleProtectionButtonOnAction(control As IRibbonControl, ByVal pressed As Boolean)
    IsThisDocumentProtected
    If pressed Then
        If Not IsProtected Then
            ActiveDocument.Protect wdAllowOnlyFormFields
        Else
            ActiveDocument.Unprotect
        End If
    Else
        If IsProtected Then
            ActiveDocument.Unprotect
        End If

    End If
    If control.Id = "ProtectOrUnprotectDocument" Then
    '    MsgBox "Got here!"
    '   This will force ribbon invalidate only for the native command
        ProtectOrUnprotectDocumentOnAction control, False
    End If
End Sub


''''' This procedure is NOT a callback, but is called from the callbacks:

Private Sub IsThisDocumentProtected()
'''' Assigns value to module-level boolean variable for use throughout the module
    IsProtected = ActiveDocument.ProtectionType <> wdNoProtection
End Sub

''''' This is NOT a callback, either, but is invoked only for particular control press:
Private Sub ProtectOrUnprotectDocumentOnAction(control As IRibbonControl, ByRef cancelDefault)
    ribbon.Invalidate
    cancelDefault = False
End Sub

限制

如果我使用内置任务窗格(查看 > 限制编辑)或以编程方式(ActiveDocument.Protect/Unprotect)保护/取消保护,我的自定义按钮将不知道更改。

这不适用于以编程方式应用的保护。至于 Review > Restrict Editing,我认为您只需要以与上面相同的方式劫持该命令的 onAction 过程,通过在 XML 中添加另一个 command 并将其引用到相同的 onAction 过程.

【讨论】:

  • 对于这种特殊情况(ProtectOrUnprotectDocument),重新调整内置按钮的用途可能会起作用。但是,我还有其他自定义控件提示作者输入各种数据,然后插入样板;他们应该听取保护状态并相应地启用/禁用。 (我希望我可以只使用内置的自动图文集库和内容控件,但 PHB 希望团队将基于 CommandBars 的传统界面“升级”为基于 Ribbon 的界面......“无需更改任何内容。”)
  • 我能够更新自定义控件的状态。 (请参阅原始帖子中的最新代码。)使功能区调用所有 get* 回调无效;在 getPressed 回调中,将是否应按下控件分配给 returnValue 参数。在去除了一些杂乱无章和重构之后,我认为我可以将混合功能代码和 UI 代码的代码臭味保持在可以容忍的水平。
  • 好吧,问题在于是否有一个事件可以让您主动监听。似乎没有这样的事件。因此,如果可能,下一个最佳解决方案是劫持ProtectOrUnprotectDocument 命令的onAction。我想我有一些对我有用的东西。请参阅修改后的答案。
  • 我已经看了很长时间,也想了很多。将观察者/pub-sub 模块组合在一起可以很好地解决这个问题,但时间很长,所以我将坚持使用原始帖子中的代码。随着时间的推移,我认为最好的方法是帮助 PHB 和办公室的其他人更多地了解内置 UI。我试试:)
猜你喜欢
  • 1970-01-01
  • 2010-09-25
  • 2019-08-10
  • 2014-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-02
  • 2018-01-06
相关资源
最近更新 更多