none
Adding a Node RRS feed

  • Question

  • Hi,

    XML is always frustrating :-(

    I have a project for mapping content controls that first creates a CustomXMLPart:

    <ccMap xmlns="http://gregmaxey/mvps.org/AddInDefaultNameSpace">
    </ccMap>
     And then adds childnodes as each CC is mapped:

    <ccMap xmlns="http://gregmaxey/mvps.org/AddInDefaultNameSpace">
     <ccElement_Name>Greg</ccElement_Name>
     <ccElement_Address>123 Maple StreetText</ccElement_Address>
    </ccMap>

    This is the code I use:

    On Error GoTo Err_Handler
    Set oCustXMLParts = ActiveDocument.CustomXMLParts
    Err_ReEnter1:
    Set oCustXMLParts = oCustXMLParts.SelectByNamespace("http://gregmaxey/mvps.org/AddInDefaultNameSpace")
    Set oCustXMLPart = oCustXMLParts(1)
    On Error GoTo 0
    'Define the XML Node that will contain the CC mapping.
    xPath = "/ns0:ccMap[1]"
    Set oCustXMLNode = oCustXMLPart.SelectSingleNode(xPath) '("/ns0:ccMap[1]")
    On Error GoTo Err_AddingNode
    'Define child node
    pStr = "ccElement_Name"
    'Define path to individual CC mapping child nodes
    xPath = "/ns0:ccMap[1]/ns0:" & pStr
    'See if the child node already exists
    Set oCustXMLChildNode = oCustXMLPart.SelectSingleNode(xPath)
    'If is doesn't then add it
    If oCustXMLChildNode Is Nothing Then
     oCustXMLPart.AddNode oCustXMLNode, pStr, "http://gregmaxey/mvps.org/AddInDefaultNameSpace", , , "Greg"
    End If
    On Error GoTo 0

    If Err.Number = 9 Then
     Set oCustXMLPart = oCustXMLParts.Add _
                ("<ccMap xmlns='" & Me.combo_NameSpace.Value & "'></ccMap>")
     Resume Err_ReEnter1
    End If

    This works well.  What I need to do is this:

    I already have a customXLMPart and I know its namespace.  Say it looks like this:

    <SomeOtherNode xmlns="SomeOtherNameSpace"></SomeOtherNode>

    I want to add a node to this customXMLPart so I can use it map content controls:

    <SomeOtherNode xmlns="SomeOtherNameSpace">
      <ccMap xmlns="SomeOtherNameSpace></ccMap>
    </SomeOtherNode>

    Here is the code I have so far:

    Set oCustXMLParts = ActiveDocument.CustomXMLParts
    Err_ReEnter1:
    Set oCustXMLParts = oCustXMLParts.SelectByNamespace("SomeOtherNameSpace")
    Set oCustXMLPart = oCustXMLParts(1)
    'Define the XML Node that will contain the CC mapping.
    xPath = "/ns0:ccMap[1]"
    Set oCustXMLNode = oCustXMLPart.SelectSingleNode(xPath) '("/ns0:ccMap[1]") 'It doesn't exist yet
    If oCustXMLNode Is Nothing Then
     'I'm stuck.
     'oCustXMLPart.AddNode ????????????
    End If

    How can I revise my code so that I can:
     1.  Use any customXMLPart in a document.
     2.  If a node ccMap doesn't exist create it
     3.  Map individual CCs to the new ccMap node

    xPath is the concept that really throws me.

    Thanks.


    Greg Maxey Please visit my website at: http://gregmaxey.mvps.org/word_tips.htm
    Monday, January 17, 2011 9:47 PM

Answers

  • Hi Greg

    There's more than one way to go about this. The test XML I used is at the end of this message. (It would really help a lot, if you expect people to test and give you sample code if you could provide some samples BTW)

    The first section shows how you can append child nodes and link them to a content control. The schema I had for testing doesn't have a namespace declaration, so no prefix. But if you need one, just comment out the line and put the prefix in you xpath.

    Sub InsertNode()
      Dim doc As word.Document
      Dim cxps As Office.CustomXMLParts
      Dim cxp As Office.CustomXMLPart
      Dim ndExisting As Office.CustomXMLNode
      Dim ndNewBook As Office.CustomXMLNode
      Dim ndNewNode As Office.CustomXMLNode
      Dim ns As String, nsPrefix As String
      Dim cc1 As word.ContentControl, cc2 As word.ContentControl
      
      ns = ""
      Set doc = ActiveDocument
      Set cxps = doc.CustomXMLParts.SelectByNamespace(ns)
      Set cxp = cxps(1)
      'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns)
      Set ndExisting = cxp.SelectSingleNode("//books")
      ndExisting.AppendChildNode Name:="book", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement
      Set ndNewBook = ndExisting.ChildNodes(ndExisting.ChildNodes.Count)
      ndNewBook.AppendChildNode Name:="title", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Test 6"
      Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count)
      Set cc1 = InsertAndMapNewCC(doc, ndNewNode)
      Debug.Print cc1.Range.Text
      
      ndNewBook.AppendChildNode Name:="author", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Us"
      Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count)
      Set cc2 = InsertAndMapNewCC(doc, ndNewNode)
      Debug.Print cc2.Range.Text
      
      Debug.Print cxp.XML  
      
      Set ndNewBook = Nothing
      Set ndExisting = Nothing
      Set cxp = Nothing
      Set cxps = Nothing
    End Sub
    
    Function InsertAndMapNewCC(doc As word.Document, nd As Office.CustomXMLNode) As word.ContentControl
      Dim cc As word.ContentControl
      Dim rng As word.Range
      
      Set rng = doc.Content
      rng.InsertAfter " "
      rng.Collapse wdCollapseEnd
      Set cc = rng.Contentcontrols.Add(wdContentControlText, rng)
      Debug.Print cc.XMLMapping.SetMappingByNode(nd)
      
      Set InsertAndMapNewCC = cc
    End Function
    

    The second set of code shows how you can insert a complete subtree of XML (a new book), rather than having to create the nodes one at a time. I don't show how to do the XML Mapping with this one, as you should be able to figure it out for yourself. It's straight XML, rather than working with nodes.

    Sub InsertTree()
      Dim doc As word.Document
      Dim cxps As Office.CustomXMLParts
      Dim cxp As Office.CustomXMLPart
      Dim ndExisting As Office.CustomXMLNode
      Dim ns As String, nsPrefix As String
      
      ns = ""
      Set doc = ActiveDocument
      Set cxps = doc.CustomXMLParts.SelectByNamespace(ns)
      Set cxp = cxps(1)
      'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns)
      Set ndExisting = cxp.SelectSingleNode("//books")
      ndExisting.InsertSubtreeBefore "<book><title>Test 4</title><author>Her</author></book>"
      Debug.Print cxp.XML
      Set ndExisting = Nothing
      Set cxp = Nothing
      Set cxps = Nothing
      
    End Sub
    
    Sub CreateBooksCustomXMLPart()
      Dim doc As word.Document
      Dim cp1 As Office.CustomXMLPart
      Dim cp2 As Office.CustomXMLPart
      Dim Xml1 As String, Xml2 As String
      
      Xml1 = "<?xml version='1.0' encoding='utf-8'?><books><book><title>Test 1</title><author>Me</author></book></books>"
      Set doc = ActiveDocument
      Set cp1 = doc.CustomXMLParts.Add(Xml1)
      doc.Variables("Xml1") = cp1.ID
      Set cp2 = doc.CustomXMLParts.Add
      cp2.Load "C:\Test\Flower.xml"
      doc.Variables("Xml2") = cp2.ID
    End Sub
    

    Cindy Meister, VSTO/Word MVP
    • Marked as answer by Bessie Zhao Thursday, January 27, 2011 10:14 AM
    Tuesday, January 18, 2011 2:51 PM
    Moderator
  • Cindy,

    Thanks again for your post.  While I had already managed a working solution, your methods were enlightening and I can use some of them in my code.  One thing I did notice is that all your child nodes are created using the same name.  In my larger project when I delete a content control in the document I want to delete its corresponding mapping node.  I have been using a unique CC title as my node name so when the node is delete it doesn't mess up the mapping of the remaining CCs.  I have put together a sample so you can observe what I mean.  Run the code passing "2" first which creates 6 CCs with unique node  names and then deletes one.  Then run the code passing "1" which creates 6 CCs using the same node name and deletes one.  You will notice the mapping of the other CCs is messed up:

    Option Explicit
    Dim oRng As Word.Range
    Sub CreateTestingCustomXMLPart()
    Dim pXML_1 As String
    Dim pXML_2 As String
    KillPart
    pXML_1 = "<?xml version='1.0' encoding='utf-8'?><ccMap xmlns='http://gregmaxey.mvps.org'></ccMap>"
    pXML_2 = "<?xml version='1.0' encoding='utf-8'?><books xmlns='http://gregmaxey.mvps.org'><book><title>Test 1</title><author>Me</author></book></books>"
    ActiveDocument.CustomXMLParts.Add (pXML_1)
    ActiveDocument.Range.Delete
    ConfigureXMLPart_BuildNodes 2
    Set oRng = ActiveDocument.Range
    With oRng
     .Copy
     .InsertAfter vbCr + vbCr
     .Collapse wdCollapseEnd
     .Paste
    End With
    Set oRng = Nothing
    DeleteCC_and_Node
    End Sub
    Sub KillPart()
    'To keep CustomXMLPart count manageable during testing
    Dim i As Long
    For i = ActiveDocument.CustomXMLParts.Count To 4 Step -1
     ActiveDocument.CustomXMLParts(i).Delete
    Next i
    End Sub
    Sub ConfigureXMLPart_BuildNodes(ByRef lngOption As Long)
    Dim oCustXMLPart As Office.CustomXMLPart
    Dim oNodeParent As Office.CustomXMLNode
    Dim oNodeCCMaps As Office.CustomXMLNode
    Dim oNodeThisCCMap As Office.CustomXMLNode
    Dim pNameSpace As String
    Dim pNSPrefix As String
    Dim oCC As Word.ContentControl
    Dim i As Long
    pNameSpace = "http://gregmaxey.mvps.org"
    Dim pName As String
    Set oCustXMLPart = ActiveDocument.CustomXMLParts.SelectByNamespace(pNameSpace).Item(1)
    pNSPrefix = oCustXMLPart.NamespaceManager.LookupPrefix(pNameSpace)
     Set oNodeParent = oCustXMLPart.SelectSingleNode(oCustXMLPart.DocumentElement.XPath)
    'See if parent (main node) is the node designated for CC mapping.
    If oNodeParent.XPath = "/" & pNSPrefix & ":ccMap[1]" Then
     'If so then use it.
     Set oNodeCCMaps = oNodeParent
    Else
     'See if a child node of the parent (main node) is designated for CC mapping
     Set oNodeCCMaps = oCustXMLPart.SelectSingleNode(oCustXMLPart.DocumentElement.XPath & "/" & pNSPrefix & ":ccMap[1]")
     'If not then create it.
     If oNodeCCMaps Is Nothing Then
       oNodeParent.AppendChildNode Name:="ccMap", NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement
       Set oNodeCCMaps = oNodeParent.ChildNodes(oNodeParent.ChildNodes.Count)
     End If
    End If

    If lngOption = 1 Then pName = "Mapped_CC"
    If lngOption = 2 Then pName = "Mapped_CC_1"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 1 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 1"
     .Tag = "CC 1 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_2"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 2 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 2"
     .Tag = "CC 2 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_3"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 3 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 3"
     .Tag = "CC 3 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_4"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 4 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 4"
     .Tag = "CC 4 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_5"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 5 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 5"
     .Tag = "CC 5 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_6"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 6 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 6"
     .Tag = "CC 6 Tag"
    End With
    Set oCC = Nothing
    Set oNodeCCMaps = Nothing
    Set oNodeParent = Nothing
    Set oCustXMLPart = Nothing
    End Sub
    Function InsertAndMapNewCC(oNode As Office.CustomXMLNode) As Word.ContentControl
    Dim oCC As Word.ContentControl

    Set oRng = Selection.Range
    Set oCC = oRng.ContentControls.Add(wdContentControlText, oRng)
    oCC.XMLMapping.SetMappingByNode oNode
    Set oRng = oCC.Range
    oRng.Collapse wdCollapseEnd
    oRng.Move wdCharacter, 2
    oRng.InsertAfter vbCr
    oRng.Move wdParagraph, 1
    oRng.Select
    Set InsertAndMapNewCC = oCC
    End Function
    Sub ScffratchMacro()
    ActiveDocument.Range.InsertAfter ActiveDocument.CustomXMLParts(4).XML
    End Sub
    Sub DeleteCC_and_Node()
    With ActiveDocument.ContentControls(3)
     Debug.Print .XMLMapping.XPath
     .XMLMapping.CustomXMLNode.Delete
     .Range.Delete
     .Delete
    End With
    End Sub

    "Cindy Meister [MVP]" wrote in message news:f6aef070-3d9a-4efa-8b20-3f64b131d328@communitybridge.codeplex.com...

    Hi Greg

    There's more than one way to go about this. The test XML I used is at the end of this message. (It would really help a lot, if you expect people to test and give you sample code if you could provide some samples BTW)

    The first section shows how you can append child nodes and link them to a content control. The schema I had for testing doesn't have a namespace declaration, so no prefix. But if you need one, just comment out the line and put the prefix in you xpath.

    Sub InsertNode() Dim doc As word.Document Dim cxps As Office.CustomXMLParts Dim cxp As Office.CustomXMLPart Dim ndExisting As Office.CustomXMLNode Dim ndNewBook As Office.CustomXMLNode Dim ndNewNode As Office.CustomXMLNode Dim ns As String, nsPrefix As String Dim cc1 As word.ContentControl, cc2 As word.ContentControl ns = "" Set doc = ActiveDocument Set cxps = doc.CustomXMLParts.SelectByNamespace(ns) Set cxp = cxps(1) 'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns) Set ndExisting = cxp.SelectSingleNode("//books") ndExisting.AppendChildNode Name:="book", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement Set ndNewBook = ndExisting.ChildNodes(ndExisting.ChildNodes.Count) ndNewBook.AppendChildNode Name:="title", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Test 6" Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count) Set cc1 = InsertAndMapNewCC(doc, ndNewNode) Debug.Print cc1.Range.Text ndNewBook.AppendChildNode Name:="author", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Us" Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count) Set cc2 = InsertAndMapNewCC(doc, ndNewNode) Debug.Print cc2.Range.Text Debug.Print cxp.XML Set ndNewBook = Nothing Set ndExisting = Nothing Set cxp = Nothing Set cxps = Nothing End Sub Function InsertAndMapNewCC(doc As word.Document, nd As Office.CustomXMLNode) As word.ContentControl Dim cc As word.ContentControl Dim rng As word.Range Set rng = doc.Content rng.InsertAfter " " rng.Collapse wdCollapseEnd Set cc = rng.Contentcontrols.Add(wdContentControlText, rng) Debug.Print cc.XMLMapping.SetMappingByNode(nd) Set InsertAndMapNewCC = cc End Function

    The second set of code shows how you can insert a complete subtree of XML (a new book), rather than having to create the nodes one at a time. I don't show how to do the XML Mapping with this one, as you should be able to figure it out for yourself. It's straight XML, rather than working with nodes.

    Sub InsertTree() Dim doc As word.Document Dim cxps As Office.CustomXMLParts Dim cxp As Office.CustomXMLPart Dim ndExisting As Office.CustomXMLNode Dim ns As String, nsPrefix As String ns = "" Set doc = ActiveDocument Set cxps = doc.CustomXMLParts.SelectByNamespace(ns) Set cxp = cxps(1) 'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns) Set ndExisting = cxp.SelectSingleNode("//books") ndExisting.InsertSubtreeBefore "<book><title>Test 4</title><author>Her</author></book>" Debug.Print cxp.XML Set ndExisting = Nothing Set cxp = Nothing Set cxps = Nothing End Sub
    Sub CreateBooksCustomXMLPart() Dim doc As word.Document Dim cp1 As Office.CustomXMLPart Dim cp2 As Office.CustomXMLPart Dim Xml1 As String, Xml2 As String Xml1 = "<?xml version='1.0' encoding='utf-8'?><books><book><title>Test 1</title><author>Me</author></book></books>" Set doc = ActiveDocument Set cp1 = doc.CustomXMLParts.Add(Xml1) doc.Variables("Xml1") = cp1.ID Set cp2 = doc.CustomXMLParts.Add cp2.Load "C:\Test\Flower.xml" doc.Variables("Xml2") = cp2.ID End Sub
    Cindy Meister, VSTO/Word MVP


    Greg Maxey Please visit my website at: http://gregmaxey.mvps.org/word_tips.htm
    • Marked as answer by Bessie Zhao Thursday, January 27, 2011 10:14 AM
    Wednesday, January 19, 2011 11:55 AM

All replies

  • Hello Greg,

     Thanks for posting. I am trying to involve someone familiar with this topic to further look at this issue. There might be some time delay. Appreciate your patience. Have a nice day.


    Bessie Zhao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, January 18, 2011 3:37 AM
  • Hi Greg

    There's more than one way to go about this. The test XML I used is at the end of this message. (It would really help a lot, if you expect people to test and give you sample code if you could provide some samples BTW)

    The first section shows how you can append child nodes and link them to a content control. The schema I had for testing doesn't have a namespace declaration, so no prefix. But if you need one, just comment out the line and put the prefix in you xpath.

    Sub InsertNode()
      Dim doc As word.Document
      Dim cxps As Office.CustomXMLParts
      Dim cxp As Office.CustomXMLPart
      Dim ndExisting As Office.CustomXMLNode
      Dim ndNewBook As Office.CustomXMLNode
      Dim ndNewNode As Office.CustomXMLNode
      Dim ns As String, nsPrefix As String
      Dim cc1 As word.ContentControl, cc2 As word.ContentControl
      
      ns = ""
      Set doc = ActiveDocument
      Set cxps = doc.CustomXMLParts.SelectByNamespace(ns)
      Set cxp = cxps(1)
      'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns)
      Set ndExisting = cxp.SelectSingleNode("//books")
      ndExisting.AppendChildNode Name:="book", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement
      Set ndNewBook = ndExisting.ChildNodes(ndExisting.ChildNodes.Count)
      ndNewBook.AppendChildNode Name:="title", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Test 6"
      Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count)
      Set cc1 = InsertAndMapNewCC(doc, ndNewNode)
      Debug.Print cc1.Range.Text
      
      ndNewBook.AppendChildNode Name:="author", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Us"
      Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count)
      Set cc2 = InsertAndMapNewCC(doc, ndNewNode)
      Debug.Print cc2.Range.Text
      
      Debug.Print cxp.XML  
      
      Set ndNewBook = Nothing
      Set ndExisting = Nothing
      Set cxp = Nothing
      Set cxps = Nothing
    End Sub
    
    Function InsertAndMapNewCC(doc As word.Document, nd As Office.CustomXMLNode) As word.ContentControl
      Dim cc As word.ContentControl
      Dim rng As word.Range
      
      Set rng = doc.Content
      rng.InsertAfter " "
      rng.Collapse wdCollapseEnd
      Set cc = rng.Contentcontrols.Add(wdContentControlText, rng)
      Debug.Print cc.XMLMapping.SetMappingByNode(nd)
      
      Set InsertAndMapNewCC = cc
    End Function
    

    The second set of code shows how you can insert a complete subtree of XML (a new book), rather than having to create the nodes one at a time. I don't show how to do the XML Mapping with this one, as you should be able to figure it out for yourself. It's straight XML, rather than working with nodes.

    Sub InsertTree()
      Dim doc As word.Document
      Dim cxps As Office.CustomXMLParts
      Dim cxp As Office.CustomXMLPart
      Dim ndExisting As Office.CustomXMLNode
      Dim ns As String, nsPrefix As String
      
      ns = ""
      Set doc = ActiveDocument
      Set cxps = doc.CustomXMLParts.SelectByNamespace(ns)
      Set cxp = cxps(1)
      'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns)
      Set ndExisting = cxp.SelectSingleNode("//books")
      ndExisting.InsertSubtreeBefore "<book><title>Test 4</title><author>Her</author></book>"
      Debug.Print cxp.XML
      Set ndExisting = Nothing
      Set cxp = Nothing
      Set cxps = Nothing
      
    End Sub
    
    Sub CreateBooksCustomXMLPart()
      Dim doc As word.Document
      Dim cp1 As Office.CustomXMLPart
      Dim cp2 As Office.CustomXMLPart
      Dim Xml1 As String, Xml2 As String
      
      Xml1 = "<?xml version='1.0' encoding='utf-8'?><books><book><title>Test 1</title><author>Me</author></book></books>"
      Set doc = ActiveDocument
      Set cp1 = doc.CustomXMLParts.Add(Xml1)
      doc.Variables("Xml1") = cp1.ID
      Set cp2 = doc.CustomXMLParts.Add
      cp2.Load "C:\Test\Flower.xml"
      doc.Variables("Xml2") = cp2.ID
    End Sub
    

    Cindy Meister, VSTO/Word MVP
    • Marked as answer by Bessie Zhao Thursday, January 27, 2011 10:14 AM
    Tuesday, January 18, 2011 2:51 PM
    Moderator
  • Hi Cindy,

    I just came back here to reply to Bessie and saw your post.  I acknowledge your mild rebuke.

    I was able to cobble together something that seems to work shown below. Still I will study your post and see where I might improve.  Thank you very much for taking time to reply,

    Basically I have a UserForm which is providing some data to a procedure that maps content controls.  The UserForm provides:

    1.  A namespace
    2.  A CC title
    3 . A CC type

    1.  The procedure looks for a CustomXMLPart that has the namespace passed. If it doesn't exist then a new CustomXMLPart is created using that namespace with a single parent node used for CC mapping
    2.  Next the procedure checks if the xPath of parent node of the CustomXMLPart is the same as the xPath designated for CC mapping.
    3.  If not the procudure adds the xPath of the parent node to the xPath of the node designated for mapping
    4.  The procedures looks for a CustomXMLNode with the xPath match the xPath desiganated for mapping.  If it doesn't exist then a CustomXMLNode is create.

    Err_ReEnter1:
    'Point to a customXMLPart that uses a unique namespace. If it doesn't exist use Error Handling to create it.
    Set oCustXMLPart = ActiveDocument.CustomXMLParts.SelectByNamespace(pNameSpace).Item(1)
    'Define a node designated for content control mapping "ccMap"
    p_xPath = "/ns0:ccMap[1]"
    'If parent node of XMLPart is not defined by xPath then modify xPath to include parent node.
    pParentNode_xPath = oCustXMLPart.SelectSingleNode(oCustXMLPart.DocumentElement.xPath).xPath
    If pParentNode_xPath <> p_xPath Then
     p_xPath = oCustXMLPart.SelectSingleNode(oCustXMLPart.DocumentElement.xPath).xPath & p_xPath
    End If
    'Point to a node designated for content control mapping "mapCC".
    Set oCustXMLNode = oCustXMLPart.SelectSingleNode(p_xPath)
    'If designated node is not present then create it.
    If oCustXMLNode Is Nothing Then
     Set oCustXMLNode = oCustXMLPart.SelectSingleNode(pParentNode_xPath)
     oCustXMLPart.AddNode oCustXMLNode, "ccMap", pNameSpace, , , ""
     Set oCustXMLNode = oCustXMLPart.SelectSingleNode(p_xPath)
    End If
    'Define child node
    pStr = "ccElement_" & Me.txtCCTitle
    pStr = ValidateScrubTitle(pStr)
    'Define child node for individual CC mapping.
    p_xPath = p_xPath & "/ns0:" & pStr
    'Point to node designated or individual CC mapping.
    Set oCustXMLChildNode = oCustXMLPart.SelectSingleNode(p_xPath)
    'If child node doesn't exist then create it.
    If oCustXMLChildNode Is Nothing Then
     oCustXMLPart.AddNode oCustXMLNode, pStr, Me.combo_NameSpace.Value, , , ""
    End If
    On Error GoTo 0
    Exit Sub
    Select Case Err.Number
     Case Is = 9
       Set oCustXMLPart = ActiveDocument.CustomXMLParts.Add _
                ("<ccMap xmlns='" & Me.combo_NameSpace.Value & "'></ccMap>")
       Resume Err_ReEnter1
    End Select
    End Sub

    "Cindy Meister [MVP]" wrote in message news:f6aef070-3d9a-4efa-8b20-3f64b131d328@communitybridge.codeplex.com...

    Hi Greg

    There's more than one way to go about this. The test XML I used is at the end of this message. (It would really help a lot, if you expect people to test and give you sample code if you could provide some samples BTW)

    The first section shows how you can append child nodes and link them to a content control. The schema I had for testing doesn't have a namespace declaration, so no prefix. But if you need one, just comment out the line and put the prefix in you xpath.

    Sub InsertNode() Dim doc As word.Document Dim cxps As Office.CustomXMLParts Dim cxp As Office.CustomXMLPart Dim ndExisting As Office.CustomXMLNode Dim ndNewBook As Office.CustomXMLNode Dim ndNewNode As Office.CustomXMLNode Dim ns As String, nsPrefix As String Dim cc1 As word.ContentControl, cc2 As word.ContentControl ns = "" Set doc = ActiveDocument Set cxps = doc.CustomXMLParts.SelectByNamespace(ns) Set cxp = cxps(1) 'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns) Set ndExisting = cxp.SelectSingleNode("//books") ndExisting.AppendChildNode Name:="book", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement Set ndNewBook = ndExisting.ChildNodes(ndExisting.ChildNodes.Count) ndNewBook.AppendChildNode Name:="title", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Test 6" Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count) Set cc1 = InsertAndMapNewCC(doc, ndNewNode) Debug.Print cc1.Range.Text ndNewBook.AppendChildNode Name:="author", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Us" Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count) Set cc2 = InsertAndMapNewCC(doc, ndNewNode) Debug.Print cc2.Range.Text Debug.Print cxp.XML Set ndNewBook = Nothing Set ndExisting = Nothing Set cxp = Nothing Set cxps = Nothing End Sub Function InsertAndMapNewCC(doc As word.Document, nd As Office.CustomXMLNode) As word.ContentControl Dim cc As word.ContentControl Dim rng As word.Range Set rng = doc.Content rng.InsertAfter " " rng.Collapse wdCollapseEnd Set cc = rng.Contentcontrols.Add(wdContentControlText, rng) Debug.Print cc.XMLMapping.SetMappingByNode(nd) Set InsertAndMapNewCC = cc End Function

    The second set of code shows how you can insert a complete subtree of XML (a new book), rather than having to create the nodes one at a time. I don't show how to do the XML Mapping with this one, as you should be able to figure it out for yourself. It's straight XML, rather than working with nodes.

    Sub InsertTree() Dim doc As word.Document Dim cxps As Office.CustomXMLParts Dim cxp As Office.CustomXMLPart Dim ndExisting As Office.CustomXMLNode Dim ns As String, nsPrefix As String ns = "" Set doc = ActiveDocument Set cxps = doc.CustomXMLParts.SelectByNamespace(ns) Set cxp = cxps(1) 'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns) Set ndExisting = cxp.SelectSingleNode("//books") ndExisting.InsertSubtreeBefore "<book><title>Test 4</title><author>Her</author></book>" Debug.Print cxp.XML Set ndExisting = Nothing Set cxp = Nothing Set cxps = Nothing End Sub
    Sub CreateBooksCustomXMLPart() Dim doc As word.Document Dim cp1 As Office.CustomXMLPart Dim cp2 As Office.CustomXMLPart Dim Xml1 As String, Xml2 As String Xml1 = "<?xml version='1.0' encoding='utf-8'?><books><book><title>Test 1</title><author>Me</author></book></books>" Set doc = ActiveDocument Set cp1 = doc.CustomXMLParts.Add(Xml1) doc.Variables("Xml1") = cp1.ID Set cp2 = doc.CustomXMLParts.Add cp2.Load "C:\Test\Flower.xml" doc.Variables("Xml2") = cp2.ID End Sub
    Cindy Meister, VSTO/Word MVP


    Greg Maxey Please visit my website at: http://gregmaxey.mvps.org/word_tips.htm
    Tuesday, January 18, 2011 6:41 PM
  • Cindy,

    Thanks again for your post.  While I had already managed a working solution, your methods were enlightening and I can use some of them in my code.  One thing I did notice is that all your child nodes are created using the same name.  In my larger project when I delete a content control in the document I want to delete its corresponding mapping node.  I have been using a unique CC title as my node name so when the node is delete it doesn't mess up the mapping of the remaining CCs.  I have put together a sample so you can observe what I mean.  Run the code passing "2" first which creates 6 CCs with unique node  names and then deletes one.  Then run the code passing "1" which creates 6 CCs using the same node name and deletes one.  You will notice the mapping of the other CCs is messed up:

    Option Explicit
    Dim oRng As Word.Range
    Sub CreateTestingCustomXMLPart()
    Dim pXML_1 As String
    Dim pXML_2 As String
    KillPart
    pXML_1 = "<?xml version='1.0' encoding='utf-8'?><ccMap xmlns='http://gregmaxey.mvps.org'></ccMap>"
    pXML_2 = "<?xml version='1.0' encoding='utf-8'?><books xmlns='http://gregmaxey.mvps.org'><book><title>Test 1</title><author>Me</author></book></books>"
    ActiveDocument.CustomXMLParts.Add (pXML_1)
    ActiveDocument.Range.Delete
    ConfigureXMLPart_BuildNodes 2
    Set oRng = ActiveDocument.Range
    With oRng
     .Copy
     .InsertAfter vbCr + vbCr
     .Collapse wdCollapseEnd
     .Paste
    End With
    Set oRng = Nothing
    DeleteCC_and_Node
    End Sub
    Sub KillPart()
    'To keep CustomXMLPart count manageable during testing
    Dim i As Long
    For i = ActiveDocument.CustomXMLParts.Count To 4 Step -1
     ActiveDocument.CustomXMLParts(i).Delete
    Next i
    End Sub
    Sub ConfigureXMLPart_BuildNodes(ByRef lngOption As Long)
    Dim oCustXMLPart As Office.CustomXMLPart
    Dim oNodeParent As Office.CustomXMLNode
    Dim oNodeCCMaps As Office.CustomXMLNode
    Dim oNodeThisCCMap As Office.CustomXMLNode
    Dim pNameSpace As String
    Dim pNSPrefix As String
    Dim oCC As Word.ContentControl
    Dim i As Long
    pNameSpace = "http://gregmaxey.mvps.org"
    Dim pName As String
    Set oCustXMLPart = ActiveDocument.CustomXMLParts.SelectByNamespace(pNameSpace).Item(1)
    pNSPrefix = oCustXMLPart.NamespaceManager.LookupPrefix(pNameSpace)
     Set oNodeParent = oCustXMLPart.SelectSingleNode(oCustXMLPart.DocumentElement.XPath)
    'See if parent (main node) is the node designated for CC mapping.
    If oNodeParent.XPath = "/" & pNSPrefix & ":ccMap[1]" Then
     'If so then use it.
     Set oNodeCCMaps = oNodeParent
    Else
     'See if a child node of the parent (main node) is designated for CC mapping
     Set oNodeCCMaps = oCustXMLPart.SelectSingleNode(oCustXMLPart.DocumentElement.XPath & "/" & pNSPrefix & ":ccMap[1]")
     'If not then create it.
     If oNodeCCMaps Is Nothing Then
       oNodeParent.AppendChildNode Name:="ccMap", NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement
       Set oNodeCCMaps = oNodeParent.ChildNodes(oNodeParent.ChildNodes.Count)
     End If
    End If

    If lngOption = 1 Then pName = "Mapped_CC"
    If lngOption = 2 Then pName = "Mapped_CC_1"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 1 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 1"
     .Tag = "CC 1 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_2"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 2 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 2"
     .Tag = "CC 2 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_3"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 3 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 3"
     .Tag = "CC 3 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_4"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 4 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 4"
     .Tag = "CC 4 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_5"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 5 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 5"
     .Tag = "CC 5 Tag"
    End With
    If lngOption = 2 Then pName = "Mapped_CC_6"
    oNodeCCMaps.AppendChildNode Name:=pName, NamespaceURI:=pNameSpace, NodeType:=msoCustomXMLNodeElement, NodeValue:="Mapped oCC 6 Text"
    Set oNodeThisCCMap = oNodeCCMaps.ChildNodes(oNodeCCMaps.ChildNodes.Count)
    Set oCC = InsertAndMapNewCC(oNodeThisCCMap)
    With oCC
     .Title = "CC 6"
     .Tag = "CC 6 Tag"
    End With
    Set oCC = Nothing
    Set oNodeCCMaps = Nothing
    Set oNodeParent = Nothing
    Set oCustXMLPart = Nothing
    End Sub
    Function InsertAndMapNewCC(oNode As Office.CustomXMLNode) As Word.ContentControl
    Dim oCC As Word.ContentControl

    Set oRng = Selection.Range
    Set oCC = oRng.ContentControls.Add(wdContentControlText, oRng)
    oCC.XMLMapping.SetMappingByNode oNode
    Set oRng = oCC.Range
    oRng.Collapse wdCollapseEnd
    oRng.Move wdCharacter, 2
    oRng.InsertAfter vbCr
    oRng.Move wdParagraph, 1
    oRng.Select
    Set InsertAndMapNewCC = oCC
    End Function
    Sub ScffratchMacro()
    ActiveDocument.Range.InsertAfter ActiveDocument.CustomXMLParts(4).XML
    End Sub
    Sub DeleteCC_and_Node()
    With ActiveDocument.ContentControls(3)
     Debug.Print .XMLMapping.XPath
     .XMLMapping.CustomXMLNode.Delete
     .Range.Delete
     .Delete
    End With
    End Sub

    "Cindy Meister [MVP]" wrote in message news:f6aef070-3d9a-4efa-8b20-3f64b131d328@communitybridge.codeplex.com...

    Hi Greg

    There's more than one way to go about this. The test XML I used is at the end of this message. (It would really help a lot, if you expect people to test and give you sample code if you could provide some samples BTW)

    The first section shows how you can append child nodes and link them to a content control. The schema I had for testing doesn't have a namespace declaration, so no prefix. But if you need one, just comment out the line and put the prefix in you xpath.

    Sub InsertNode() Dim doc As word.Document Dim cxps As Office.CustomXMLParts Dim cxp As Office.CustomXMLPart Dim ndExisting As Office.CustomXMLNode Dim ndNewBook As Office.CustomXMLNode Dim ndNewNode As Office.CustomXMLNode Dim ns As String, nsPrefix As String Dim cc1 As word.ContentControl, cc2 As word.ContentControl ns = "" Set doc = ActiveDocument Set cxps = doc.CustomXMLParts.SelectByNamespace(ns) Set cxp = cxps(1) 'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns) Set ndExisting = cxp.SelectSingleNode("//books") ndExisting.AppendChildNode Name:="book", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement Set ndNewBook = ndExisting.ChildNodes(ndExisting.ChildNodes.Count) ndNewBook.AppendChildNode Name:="title", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Test 6" Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count) Set cc1 = InsertAndMapNewCC(doc, ndNewNode) Debug.Print cc1.Range.Text ndNewBook.AppendChildNode Name:="author", NamespaceURI:=ns, NodeType:=msoCustomXMLNodeElement, NodeValue:="Us" Set ndNewNode = ndNewBook.ChildNodes(ndNewBook.ChildNodes.Count) Set cc2 = InsertAndMapNewCC(doc, ndNewNode) Debug.Print cc2.Range.Text Debug.Print cxp.XML Set ndNewBook = Nothing Set ndExisting = Nothing Set cxp = Nothing Set cxps = Nothing End Sub Function InsertAndMapNewCC(doc As word.Document, nd As Office.CustomXMLNode) As word.ContentControl Dim cc As word.ContentControl Dim rng As word.Range Set rng = doc.Content rng.InsertAfter " " rng.Collapse wdCollapseEnd Set cc = rng.Contentcontrols.Add(wdContentControlText, rng) Debug.Print cc.XMLMapping.SetMappingByNode(nd) Set InsertAndMapNewCC = cc End Function

    The second set of code shows how you can insert a complete subtree of XML (a new book), rather than having to create the nodes one at a time. I don't show how to do the XML Mapping with this one, as you should be able to figure it out for yourself. It's straight XML, rather than working with nodes.

    Sub InsertTree() Dim doc As word.Document Dim cxps As Office.CustomXMLParts Dim cxp As Office.CustomXMLPart Dim ndExisting As Office.CustomXMLNode Dim ns As String, nsPrefix As String ns = "" Set doc = ActiveDocument Set cxps = doc.CustomXMLParts.SelectByNamespace(ns) Set cxp = cxps(1) 'nsPrefix = cxp.NamespaceManager.LookupPrefix(ns) Set ndExisting = cxp.SelectSingleNode("//books") ndExisting.InsertSubtreeBefore "<book><title>Test 4</title><author>Her</author></book>" Debug.Print cxp.XML Set ndExisting = Nothing Set cxp = Nothing Set cxps = Nothing End Sub
    Sub CreateBooksCustomXMLPart() Dim doc As word.Document Dim cp1 As Office.CustomXMLPart Dim cp2 As Office.CustomXMLPart Dim Xml1 As String, Xml2 As String Xml1 = "<?xml version='1.0' encoding='utf-8'?><books><book><title>Test 1</title><author>Me</author></book></books>" Set doc = ActiveDocument Set cp1 = doc.CustomXMLParts.Add(Xml1) doc.Variables("Xml1") = cp1.ID Set cp2 = doc.CustomXMLParts.Add cp2.Load "C:\Test\Flower.xml" doc.Variables("Xml2") = cp2.ID End Sub
    Cindy Meister, VSTO/Word MVP


    Greg Maxey Please visit my website at: http://gregmaxey.mvps.org/word_tips.htm
    • Marked as answer by Bessie Zhao Thursday, January 27, 2011 10:14 AM
    Wednesday, January 19, 2011 11:55 AM