CustomXmlParts.vb
''
'' This code is part of Document Solutions for Word demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports System.Collections.Generic
Imports System.Linq
Imports System.Xml
Imports GrapeCity.Documents.Word

'' This example demonstrates how to create a document with multiple ContentControls,
'' whose values are mapped to user-created CustomXmlPart file, standart Xml file
'' which defines various elements And attributes.
'' This document also contain a header And a footer with elements that are also mapped to
'' proper xml elements.
Public Class CustomXmlParts
    Function CreateDocx() As GcWordDocument
        Dim doc = New GcWordDocument()

        '' Create custom xml part And keep it in the document
        CreateOrderCustomXmlFromString(doc)

        '' Set custom xml document element value
        SetDeliveryOption(doc, True)

        '' Compose document with Case properties bound to proper CustomControls

        '' Create And map To text field
        CreateOficialBody(doc)

        '' Create And map Appeal checkbox And AppealDeadline Text control
        CreateDeliveryCheckbox(doc)

        '' Create Header And map Department xml element
        CreateHeaderWithMappedDepartment(doc)

        '' Create Header And map document built-in Author xml element
        CreateFooterWithMappedAuthor(doc)

        '' Done
        Return doc
    End Function

    '' Retrieve a CustomXmlPart from a document by name
    Private Shared Function GetCustomXmlPartByName(ByRef doc As GcWordDocument, ByRef name As String) As CustomXmlPart
        For Each xmlPart In doc.CustomXmlParts
            If String.Compare(xmlPart.XmlDocument.DocumentElement.Name, name, True) = 0 Then
                Return xmlPart
            End If
        Next
        Throw New ArgumentException(String.Format("Could not find custom xml part {0}", name))
    End Function


    '' Find 'Order' CustomXmlPart and set its DeliveryAppeal xml element to value provided by method parameter:
    Private Shared Sub SetDeliveryOption(ByRef doc As GcWordDocument, ByVal deliveryRequired As Boolean)
        Dim xmlPart = GetCustomXmlPartByName(doc, "Order")

        Dim node = xmlPart.XmlDocument.SelectSingleNode("//*[local-name()='Delivery']")
        If deliveryRequired Then
            node.InnerText = "true"
        Else
            node.InnerText = "false"
        End If
    End Sub

    '' Compose half of document body, map "To" text custom control to proper CustomXml field
    Private Shared Sub CreateOficialBody(ByRef doc As GcWordDocument)
        Dim p = doc.Body.Paragraphs.Add("Dear ")
        Dim tto = p.GetRange().ContentControls.Add(ContentControlType.Text, False)
        tto.XmlMapping.SetMapping("//grapecity:To", "xmlns:grapecity='http://grapecity.com'")

        p.GetRange().Runs.First.Font.Size = 10
        tto.Font.Size = 10
        p.GetRange().Runs.Add(",")
        doc.Body.Paragraphs.Add()

        p = doc.Body.Paragraphs.Add(
            "The first shipment of equipment from AMA Ltd has arrived. " +
            "We are delighted with every piece. Therefore, we decided to make " +
            "our initial purchase larger than anticipated. I am attaching our " +
            "purchase order No. 8393 for additional goods. Since you already have " +
            "a copy of our Procurement Guidelines, I shall not attach them to " +
            "this order. Current Shipping date is ")

        '' Add shipping Date control, bound to proper section of Order CustomXmlPart:
        Dim dateControl = p.GetRange().ContentControls.Add(ContentControlType.Date, False)
        dateControl.DateFormat = "dd.MM.yyyy"
        ''here we demonstrate how to map CustomControl with namespaces usage, it allows us write
        ''clean And straight XmlPath. Its the right way of XMlMapping
        dateControl.XmlMapping.SetMapping("//grapecity:Delivery/@DeliveryDate",
            "xmlns:grapecity='http://grapecity.com'")
        p.GetRange().Paragraphs.Add(
            "If you want to change it, select checkbox below and change Date, " +
            "then send this letter back to me")
        dateControl.Font.Italic = True

        p.GetRange().Runs.First.Font.Size = 12
        p.GetRange().Runs.First.Font.Italic = True
    End Sub

    '' Create footer And map Author built-in property to Text content control
    Private Shared Sub CreateFooterWithMappedAuthor(ByRef doc As GcWordDocument)
        Dim footer = doc.Body.Sections.First.Footers(HeaderFooterType.Primary)
        Dim p = footer.Body.Paragraphs.Add("Sincerely yours, ")

        If p.Document.Settings.BuiltinProperties.Author Is Nothing Then
            p.Document.Settings.BuiltinProperties.Author = "Nancy Davolio"
        End If

        '' Create Text ContentControl And bind its value to builtin Author property:
        Dim authorTextControl = p.GetRange().ContentControls.Add(ContentControlType.Text, False)

        '' Use specialized SetMapping overload for document BuiltInProperties.
        '' here we bind (map) ContentControl to builtin Author property:
        authorTextControl.XmlMapping.SetMapping(Function() p.Document.Settings.BuiltinProperties.Author)
    End Sub

    '' Create header with Text ContentControl mapped to 'Department' node of 'Order' CustomXml:
    Private Shared Sub CreateHeaderWithMappedDepartment(ByRef doc As GcWordDocument)
        '' Create Header And fill it with data:
        Dim header = doc.Body.Sections.First.Headers(HeaderFooterType.Primary)
        Dim p = header.Body.Paragraphs.Add()
        Dim departmentTextControl = p.GetRange().ContentControls.Add(ContentControlType.Text, False)

        '' Get our Case CustomXmlPart:
        Dim xmlPart = GetCustomXmlPartByName(doc, "Order")

        Dim departmentNode = xmlPart.XmlDocument.SelectSingleNode("//*[local-name()='Department']")
        '' Mapping directly to XmlNode:
        departmentTextControl.XmlMapping.SetMapping(departmentNode)
    End Sub

    '' Create Delivery part, Checkbox custom control bound to Delivery node And Date custom control
    '' bound to details of (possible) Delivery date.
    Private Shared Sub CreateDeliveryCheckbox(ByRef doc As GcWordDocument)
        Dim p = doc.Body.Paragraphs.Add()

        '' Create checkbox And map it to Shipping node of 'Case' CustomXmlPart:
        Dim checkBox = p.GetRange().ContentControls.Add(ContentControlType.CheckBox, False)

        '' Here we demonstrate how to map CustomControl ignoring namespaces used in the xml.
        '' This way should be avoided as much as possible as pretty inefficient:
        checkBox.XmlMapping.SetMapping("//*[local-name()='Delivery']")

        p.GetRange().Runs.Add("Delivery should be done before ")

        '' Add shipping Date control, bound to proper section of Order CustomXmlPart:
        Dim dateControl = p.GetRange().ContentControls.Add(ContentControlType.Date, False)
        dateControl.DateFormat = "dd.MM.yyyy"
        '' Here we demonstrate how to map CustomControl with namespaces usage, it allows us write
        '' clean And straight XmlPath. Its the right way of XMlMapping:
        dateControl.XmlMapping.SetMapping("//grapecity:Delivery/@DeliveryDate",
            "xmlns:grapecity='http://grapecity.com'")
    End Sub

    '' Create XML from a string (see alternative method below)
    Private Shared Sub CreateOrderCustomXmlFromString(ByRef doc As GcWordDocument)
        Dim sb = New System.Text.StringBuilder(201)
        Const ns = "'http://grapecity.com'"

        sb.AppendLine("<Order xmlns=" + ns + ">")
        sb.AppendLine("  <To>Mark Donahue</To>")
        sb.AppendLine("  <Department>Shipping department</Department>")
        sb.AppendLine("  <Delivery DeliveryDate=""12.04.2019"">true</Delivery>")
        sb.AppendLine("</Order>")

        Dim xml = New XmlDocument()
        xml.LoadXml(sb.ToString())
        Dim xmlPart = doc.CustomXmlParts.Add(xml, Guid.NewGuid().ToString(), New String() {ns}, "application/xml")
    End Sub

    '' Create XML by adding individual nodes one by one
    '' (this method Is Not used in this sample, the previous
    '' alternative Is used instead)
    Private Shared Sub CreateOrderCustomXml(ByRef doc As GcWordDocument)
        '' add a custom xml part with custom settings
        Const ns = "http://grapecity.com"
        Dim xml = New XmlDocument()

        xml.AppendChild(xml.CreateXmlDeclaration("1.0", "utf-8", Nothing))
        Dim root = xml.CreateElement("Order", ns)
        xml.AppendChild(root)

        Dim tto = root.AppendChild(xml.CreateElement("To", ns))
        tto.InnerText = "Mark Donahue"

        Dim dep = root.AppendChild(xml.CreateElement("Department", ns))
        dep.InnerText = "Shipping department"

        Dim child = xml.CreateElement("Delivery", ns)
        child.InnerText = "true"
        child.SetAttribute("DeliveryDate", "12.04.2019")

        root.AppendChild(child)

        Dim xmlPart = doc.CustomXmlParts.Add(xml, Guid.NewGuid().ToString(), New String() {ns}, "application/xml")

        '' Sad story: we cannot apply schemas To our custom xml parts.
        '' Unlike in Office excel (where a schema can be serialized inside a document),
        '' it seems that Word can work with schema URIs only so we cannot place an xsd inside the document.
        '' This means that we cannot implement native xml-mapping restrictiong And verifying Like
        '' xmlPart.Schemas.Add(CreateCelebrationScheme())
    End Sub
End Class