Trapping events on a repeating section content control

[This is the follow-up to a previous post about mapping repeating content controls to a custom XML part.]
As a developer, you might want to be notified when the user adds a new set of mapped nodes to a repeating section, or deletes a set. If you look for events for Content Controls to help you with this you won’t find any.

Nor are there any events at the document or application level.

And you can’t re-purpose the entries placed in the context menu for adding or removing a repeating section item.

So, what can you do?

The only possibility is to make sure the repeating content control is mapped to a Custom XML Part. When this is the case, you can use the events of the CustomXMLPart to trap when nodes are added or deleted to the part. When the user chooses to add or delete an item in a repeating section, this also adds or deletes nodes in the custom XML part.

There are three events associated with custom XML parts: NodeAfterDelete, NodeAfterAdd and NodeAfterReplace. These run at the application level, not the document level, like content controls.

For working with events at the application level, you need to insert a class module into your VBA project. RepSec5
At the top of the code module, you declare the type of object whose events you want to work with, including the keyword WithEvents in the declaration: Public WithEvents cxp As Office.CustomXMLPart.

Once you’ve done that, the events appropriate to the declaration will be available in the dropdown list at the top, right of the code window. Simply choose the event(s) you want to work from and skeleton code will be inserted with all the necessary parameters. Just add the code you want to have executed when the event fires.RepSec6

Before the events can fire, you still have to initiate them, which is done in a regular module. Since the event should stay active after the procedure finishes, you need to declare an object for the class at the top level of the module, a so-called “global variable”: Public m_Cxp As clsRepSecCC.

In the code you instantiate the variable and assign the CustomXMLPart to it for which you want to trap the events:
Set m_Cxp = New clsRepSecCC
Set m_Cxp.cxp = cxp

The entire code follows. You can download sample Zip file with the entire repeating section document for this and the previous blog post on the topic.

'Code in class module
Public WithEvents cxp As Office.CustomXMLPart

Private Sub cxp_NodeAfterDelete(ByVal OldNode As Office.CustomXMLNode, _
  ByVal OldParentNode As Office.CustomXMLNode, _
  ByVal OldNextSibling As Office.CustomXMLNode, ByVal InUndoRedo As Boolean)
  Dim sNextSiblingXML As String
  If Not OldNextSibling Is Nothing Then
    sNextSiblingXML = OldNextSibling.XML
    sNextSiblingXML = "[no node followed]"
  End If
  If Not InUndoRedo Then
    MsgBox "The node " & OldNode.BaseName & " was deleted from the parent node " _
        & OldParentNode.XPath & ". It preceded the node: " & vbCr & _
    End If
End Sub

Private Sub cxp_NodeAfterInsert(ByVal NewNode As Office.CustomXMLNode, _
  ByVal InUndoRedo As Boolean)
    If Not InUndoRedo Then
        MsgBox "A new node was inserted at: " & vbCr & _
            NewNode.XPath & vbCr & "with the following information:" & vbCr & _
        MsgBox "The node " & NewNode.BaseName & _
            " was inserted into the document during Undo/Redo."
    End If
End Sub

'Code in regular module
Public m_Cxp As clsRepSecCC

Sub DemoRepSecCC()
    Dim doc As Word.Document
    Dim sPathXML As String
    Dim cxp As Office.CustomXMLPart
    Set doc = ActiveDocument
    sPathXML = doc.Path & "\BookList.xml"
    Set cxp = CreateCustomXMLPart(doc, sPathXML)
    Debug.Print MapContentControls(doc, cxp)
    Set m_Cxp = New clsRepSecCC
    Set m_Cxp.cxp = cxp
End Sub

Private Function CreateCustomXMLPart(doc As Word.Document, sPathXML As String) _
                 As Office.CustomXMLPart
    Dim cxp As Office.CustomXMLPart
    Set cxp = doc.CustomXMLParts.Add
    cxp.Load sPathXML
    Set CreateCustomXMLPart = cxp
End Function

Private Function MapContentControls(doc As Word.Document, _
                 cxp As Office.CustomXMLPart) As Boolean
    Dim rng As Word.Range
    Dim ccRepSec As Word.ContentControl
    On Error GoTo Errhandler
    Set rng = doc.Tables(1).Rows(2).Range
    doc.SelectContentControlsByTitle("Title").Item(1).XMLMapping.SetMapping _
      "//title", , cxp
    doc.SelectContentControlsByTitle("Publisher").Item(1).XMLMapping.SetMapping _
      "//publisher", , cxp
    doc.SelectContentControlsByTitle("ISBN").Item(1).XMLMapping.SetMapping _
      "//isbn", , cxp
    doc.SelectContentControlsByTitle("Author").Item(1).XMLMapping.SetMapping _
      "//author", , cxp
    doc.SelectContentControlsByTitle("Authors").Item(1).XMLMapping.SetMapping _
      "//authors/author", , cxp
    Set ccRepSec = rng.ContentControls.Add( _
        Word.WdContentControlType.wdContentControlRepeatingSection, rng)
    ccRepSec.XMLMapping.SetMapping "//books/book", , cxp
    MapContentControls = True
Errhandler: 'Function will not return true if an error is encountered
End Function

Leave a Reply