首页 > 解决方案 > 关闭文档并重新打开后自定义 UI 编辑器功能区未加载

问题描述

我需要一些有关 Word 中功能区的自定义 UI 编辑器代码的帮助。我在 Word 2010 中使用的相同代码在 Word 2019 中不再适用。当我根据模板创建文档时,功能区就在那里并且可以工作。我保存文档并关闭它。当我重新打开功能区时,就像“死了”。它没有被激活,代码不再运行。

Word 2019 发生了什么变化?我需要做什么来纠正这个问题?

这是自定义 UI 代码:

<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<!--This is the Expense Template-->
    
    <ribbon>
        <tabs>
            <tab id="customTab3" label="Expense">
                <group id="customGroup110" label="Expense/Disbursement Calculate">
                    <button id="customButton110" size="large" label="Expense/Disbursement Calculate" imageMso="CreateReport" onAction="ExpenseCalculate" />
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI> 

这是模板中的代码:

Sub ExpenseCalculate(control As IRibbonControl)

' Edited Code On: 2/6/2012
' Edited Code By: Sheila Shines
' Code Change: Added code to test to see what Template a user is in before running code
    
    Dim rowz As Integer
    Dim theRow As Integer
    Dim rT As Integer
    Dim myTemplate As Template

    Set myTemplate = ActiveDocument.AttachedTemplate
    
    If UCase(myTemplate.Name) = "EXPENSE.DOTM" Or UCase(myTemplate.Name) = "EXPENSE.DOT" Then
        'MOVES TO BEGINNING OF DOCUMENT
        Selection.HomeKey unit:=wdStory, Extend:=wdMove

        'LINE DOWN
        Selection.MoveDown unit:=wdLine, Count:=1, Extend:=wdMove

        'LINE DOWN UNTIL INTO A TABLE
        While Selection.Information(wdWithInTable) = 0
            Selection.MoveDown unit:=wdLine, Count:=1, Extend:=wdMove
        Wend
    
        'MOVE TO START OF ROW OF THE TABLE
        Selection.StartOf unit:=wdRow, Extend:=wdMove
    
        'SELECTING TABLE
        ActiveDocument.Tables(1).Select
    
        'NUMBER OF ROWS IN THE TABLE
        rowz = Selection.Information(wdMaximumNumberOfRows)
    
        'MOVING LEFT ONE PLACE
        Selection.MoveLeft unit:=wdCharacter, Count:=1, Extend:=wdMove
    
        'ROW CONTAINING THE BEGINNING OF THE SELECTION
        theRow = Selection.Information(wdStartOfRangeRowNumber)
    
        'MOVING DOWN ONE LINE AT A TIME THROUGH TABLE
        While theRow < rowz
            Selection.MoveDown unit:=wdLine, Count:=1, Extend:=wdMove
            theRow = Selection.Information(wdStartOfRangeRowNumber)
        Wend
    
        'MOVING OVER TWO CELLS
        Selection.Move unit:=wdCell, Count:=2
    
        'DELETING INFORMATION OUT OF CELL IF ANY
        Selection.Range.Delete
    
        rowz = rowz - 1
        rT = Right$(Str$(rowz), Len(Str$(rowz)) - 1)
    
        'INSERTING FIELD INTO TABLE
        Selection.InsertFormula Formula:="=sum(c2:c" & CInt(rT) & ")", NumberFormat:="$#,##0.00;($#,##0.00)"
    
        'PRINTING DOCUMENT
        ActiveDocument.PrintOut
    Else
        MsgBox "You need to be in an Expense template in order to use this macro", vbCritical, "In wrong template"
        End
    End If
End Sub

Here is the solution that I have tried

What happens to the Word session ribbon after closing and reopening a document with a custom ribbon?

https://stackoverflow.com/questions/57841404/what-happens-to-the-word-session-ribbon-after-closing-and-reopening-a-document-w

标签: vbams-wordribbonx

解决方案


您尝试的解决方案看起来是一个真正的组合,绝对不是我推荐的东西。但由于它部分地为您工作,它证实了我第一次阅读您的问题时的直觉,即您的功能区需要失效(刷新)。

这里有一个答案可以提供您需要的代码,尽管我有一些问题。

在这一点上,我必须解释我没有在文档模板中放置功能区或代码(我将所有代码放在全局模板中,然后使用功能区回调来确定哪些控件应该可见/启用)所以我不是确定这是否适合您。

要完成这项工作,您需要在加载时获得指向功能区的指针。为此,您需要一个 onLoad 回调和相应的功能区 xml:

<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="ribbonOnLoad">

回调模块(顺便说一句,我冒昧地重写了您的代码):

Option Explicit

'CopyMemory for ribbon retrieval
#If Win64 Then
Declare PtrSafe Sub CopyMemory Lib "kernel32" _
  Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
#Else
Public Declare Sub CopyMemory Lib "kernel32" _
  Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
#End If

Private rbnUI                As IRibbonUI

Public Property Get RibbonUI() As IRibbonUI
#If Win64 Then
  Dim lngRbnPtr                   As LongPtr
#Else
  Dim lngRbnPtr                   As Long
#End If
  Dim objRibbon                   As Object

  If rbnUI Is Nothing Then
    'the pointer is lost so retrieve it from the registry
    lngRbnPtr = GetSetting("Templates", "Ribbon", "Ribbon Pointer WD")
    CopyMemory objRibbon, lngRbnPtr, 4
    Set rbnUI = objRibbon
    ' clean up invalid object
    CopyMemory objRibbon, 0&, 4
    Set objRibbon = Nothing

  End If

  Set RibbonUI = rbnUI

End Property

Public Property Set RibbonUI(ribbon As IRibbonUI)
#If Win64 Then
  Dim lngRbnPtr                   As LongPtr
#Else
  Dim lngRbnPtr                   As Long
#End If

  Set rbnUI = ribbon
  lngRbnPtr = ObjPtr(ribbon)
  'save pointer to registry for safe keeping
  SaveSetting "Templates", "Ribbon", "Ribbon Pointer WD", lngRbnPtr
End Property

Private Sub ribbonOnLoad(ribbon As IRibbonUI)
  ' Store pointer to IRibbonUI
  Set RibbonUI = ribbon
End Sub

Public Sub ribbonInvalidate()
  On Error GoTo ProcError

  RibbonUI.Invalidate

ProcExit:
  'Clean up
  On Error Resume Next
  Exit Sub

ProcError:
  If Err.Number = 91 Then
    MsgBox "Unrecoverable Ribbon Error" & vbCrLf & "" & vbCrLf & _
      "Unable to refresh the ribbon. Please save and reopen " & Application.name & _
      ".", vbOKOnly + vbExclamation, "Ribbon Error"
  Else
    'add your own
  End If
End Sub

Sub ExpenseCalculate(control As IRibbonControl)

    Dim myTemplate As Template

    Set myTemplate = ActiveDocument.AttachedTemplate
    
    If UCase(myTemplate.name) = "EXPENSE.DOTM" Or UCase(myTemplate.name) = "EXPENSE.DOT" Then
        'INSERTING FIELD INTO TABLE
        With ActiveDocument.Tables(1).Rows.Last.Cells(3)
          .Range.Delete
          .Formula Formula:="=sum(above)", NumFormat:="$#,##0.00;($#,##0.00)"
        End With
    
        'PRINTING DOCUMENT
        ActiveDocument.PrintOut
    Else
        MsgBox "You need to be in an Expense template in order to use this macro", vbCritical, "In wrong template"
        End
    End If
  Set myTemplate = Nothing
End Sub

接下来,您需要一种使功能区无效的方法。为此,我使用了一个与 Word 的应用程序事件挂钩的类模块。这使我能够在每次ActiveDocument更改时使功能区无效,即创建或打开文档,在打开的文档之间切换。

Option Explicit

Public WithEvents appWord           As Word.Application

Private Sub appWord_DocumentChange()
  InvalidateRibbon
End Sub

加载模板时需要初始化类模块。这可以使用 AutoExec 例程来完成。我喜欢将实际初始化保存在一个单独的例程中,以便我可以从全局错误处理例程中调用它。

Public wordEvents               As clsWordEvents

Public Sub AutoExec()
  instantiateEventHandler

End Sub

Public Sub instantiateEventHandler()
  If wordEvents Is Nothing Then
    Set wordEvents = New clsWordEvents
    Set wordEvents.appWord = Word.Application
  End If
End Sub

推荐阅读