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.
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.
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 Else 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 & _ sNextSiblingXML 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 & _ NewNode.XML Else 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