none
Repeating Section Content Control not updating Custom XML RRS feed

  • Question

  • I am trying to make a word template with content control pointing to customXML. 

    The idea is to produce an employee training record that uses content control to gather details on staff experience and skill that we can later parse into our databases.

    I have managed to get most of the content controls to work but am struggling with the repeating section. I want the user to be able to add new "experience" items and have the new "experience" written to the customXML.

    When I test the document I have a few things I'm struggling to fix:

    1. The repeating control doesn't populate the two "experience" nodes in the existing XML.
    2. Clicking the add repeating section adds the new section and updates the structure of the XML file, but doesn't populate the customXML with the content.

    I was hoping this would be a quick and easy thing but I'm now banging my head against my desk. Help graciously welcomed! (And if it's because I've done something stupid.. please be gentle).

    Embedded custom XML

    <?xml version="1.0" encoding="utf-8" ?>
    <employees xmlns="http://schemas.mydomain.com/qms">
      <employee>
        <employeeName>A.N. Other</employeeName>
        <employeeNumber>001</employeeNumber>
        <employeeRole>Laboratory Technician</employeeRole>
        <employeeStartDate>2018-05-29</employeeStartDate>
        <employeeDepartment>Production</employeeDepartment>
        <experience experienceTitle="Lab Tech">
          <experienceDescription>I was a lab tech</experienceDescription>
          <experienceStart>2014-01-01</experienceStart>
          <experienceEnd>2015-03-01</experienceEnd>
        </experience>
        <experience experienceTitle="Lab Tech 2">
          <experienceDescription>I was a lab tech 2</experienceDescription>
          <experienceStart>2015-04-01</experienceStart>
          <experienceEnd>2018-03-01</experienceEnd>
        </experience>
      </employee>
    </employees>
    


    Schema

    <?xml version="1.0" encoding="utf-8"?>
    <xs:schema id="TrainingRecordSchema"  xmlns="http://schemas.mydomain.com/qms" targetNamespace="http://schemas.mydomain.com/qms" xmlns:xs="http://www.w3.org/2001/XMLSchema"    elementFormDefault="qualified"       >
      <xs:element name="employees" type="EmployeesType"></xs:element>
      <xs:complexType name="EmployeesType">
        <xs:all>
          <xs:element name="employee" type="EmployeeType"/>
        </xs:all>
      </xs:complexType>
      
      
      <xs:complexType name="EmployeeType">
        <xs:sequence>
          <xs:element name="employeeName" type="xs:string" minOccurs="1" maxOccurs="1" />
          <xs:element name="employeeNumber" type="xs:integer" minOccurs="1" maxOccurs="1" />
          <xs:element name="employeeRole" type="xs:string" minOccurs="1" maxOccurs="1" />
          <xs:element name="employeeStartDate" type="xs:date" minOccurs="1" maxOccurs="1" />
          <xs:element name="employeeDepartment" type="DepartmentType" minOccurs="1" maxOccurs="1" />
          <xs:element name="experience" type="EmployeeExperience" minOccurs="1" maxOccurs ="unbounded"/>
        </xs:sequence>
      </xs:complexType>
      
      <xs:complexType name="EmployeeExperience">
        <xs:sequence>
          <xs:element name="experienceDescription" type="xs:string"   />
          <xs:element name="experienceStart" type="xs:string"  />
          <xs:element name="experienceEnd" type="xs:string"  />
        </xs:sequence>
        <xs:attribute name="experienceTitle" type="xs:string"   />
      </xs:complexType>
        
      <xs:simpleType name="DepartmentType">
        <xs:restriction base="xs:string">
          <xs:enumeration value="Core" />
          <xs:enumeration value="R&amp;D Engineering" />
          <xs:enumeration value="Production" />
        </xs:restriction>
      </xs:simpleType>
    </xs:schema>
    

    Code

    namespace Training_Records
    {
        public partial class TrainingRecord
        {
            [CachedAttribute()]
            public string employeeXMLPartID = string.Empty;
            private Office.CustomXMLPart employeeXMLPart;
            private const string prefix = "xmlns:ns='http://schemas.mydomain.com/qms'";
    
    
            private void ThisDocument_Startup(object sender, System.EventArgs e)
            {
                string xmlData = GetXmlFromResource();
    
                if (xmlData != null)
                {
                    AddCustomXmlPart(xmlData);
                    BindStaticControlsToCustomXmlPart();
                }
            }
    
            private void ThisDocument_Shutdown(object sender, System.EventArgs e)
            {
            }
    
            #region VSTO Designer generated code
    
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InternalStartup()
            {
                this.Startup += new System.EventHandler(ThisDocument_Startup);
                this.Shutdown += new System.EventHandler(ThisDocument_Shutdown);
            }
    
            #endregion
    
            #region Content Management
            private string GetXmlFromResource()
            {
                System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
                string[] resList = asm.GetManifestResourceNames();
                System.IO.Stream stream1 = asm.GetManifestResourceStream("Training_Records.employees.xml");
    
                using (System.IO.StreamReader resourceReader = new System.IO.StreamReader(stream1))
                {
                    if (resourceReader != null)
                    {
                        return resourceReader.ReadToEnd();
                    }
                }
                return null;
            }
           
           private void AddCustomXmlPart(string xmlData)
            {
                if (xmlData != null)
                {
                    employeeXMLPart = this.CustomXMLParts.SelectByID(employeeXMLPartID);
                    if (employeeXMLPart == null)
                    {
                        employeeXMLPart = this.CustomXMLParts.Add(xmlData);
                        employeeXMLPart.NamespaceManager.AddNamespace("ns", @"http://schemas.mydomain.com/qms");
                        employeeXMLPartID = employeeXMLPart.Id;
    
                    }
                }
            }
            private void BindStaticControlsToCustomXmlPart()
            {
              
                string xPathName = "ns:employees/ns:employee/ns:employeeName";
                this.ccEmployeeName.XMLMapping.SetMapping(xPathName, prefix, employeeXMLPart);
    
                string xPathNumber = "ns:employees/ns:employee/ns:employeeNumber";
                this.ccEmployeeNumber.XMLMapping.SetMapping(xPathNumber, prefix, employeeXMLPart);
    
                string xPathRoleTitle = "ns:employees/ns:employee/ns:employeeRoleTitle";
                this.ccRoleTitle.XMLMapping.SetMapping(xPathRoleTitle, prefix, employeeXMLPart);
    
                string xPathDate = "ns:employees/ns:employee/ns:employeeStartDate";
                this.ccEmploymentStartDate.DateDisplayFormat = "dd/mm/yyyy";
                this.ccEmploymentStartDate.XMLMapping.SetMapping(xPathDate, prefix, employeeXMLPart);
    
                string xPathDept = "ns:employees/ns:employee/ns:employeeDepartment";
                this.ccEmployeeDepartment.XMLMapping.SetMapping(xPathDept, prefix, employeeXMLPart);
    
                // The repeating element
                string xPathExpRep = "ns:employees/ns:employee/ns:experience[*]";
                this.ccExpRep.XMLMapping.SetMapping(xPathExpRep, prefix, employeeXMLPart);
               
                string xPathExperience = "ns:employees/ns:employee/ns:experience/@experienceTitle";
                this.CCExpTitle.XMLMapping.SetMapping(xPathExperience, prefix, employeeXMLPart);
    
                string xPathExpDesc = "ns:employees/ns:employee/ns:experience/ns:experienceDescription";
                this.ccExpDesc.XMLMapping.SetMapping(xPathExpDesc, prefix, employeeXMLPart);
    
                string xPathSDate = "ns:employees/ns:employee/ns:experience/ns:experienceStart";
                this.ccExpSDate.DateDisplayFormat = "dd/MM/yyyy";
                this.ccExpSDate.XMLMapping.SetMapping(xPathSDate, prefix, employeeXMLPart);
    
                string xPathFDate = "ns:employees/ns:employee/ns:experience/ns:experienceEnd";
                this.ccExpFDate.DateDisplayFormat = "dd/MM/yyyy";
                this.ccExpFDate.XMLMapping.SetMapping(xPathFDate, prefix, employeeXMLPart);
            }
            #endregion
            
        }
    }
    

    Wednesday, May 30, 2018 2:41 PM

All replies

  • Hi,

    Based on your description, I will move your thread to Word for Developer forum:

    https://social.msdn.microsoft.com/Forums/en-US/home?forum=worddev

    The reason why we recommend posting appropriately is you will get the most qualified pool of respondents, and other partners who read the forums regularly can either share their knowledge or learn from your interaction with us. Thank you for your understanding.

    Regards,

    Emi


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnsf@microsoft.com.


    Click here to learn more. Visit the dedicated forum to share, explore and talk to experts about Microsoft Teams.

    Thursday, May 31, 2018 7:50 AM
  • There was a discussion of a similar problem here: https://social.msdn.microsoft.com/Forums/en-US/f7a6c4a7-ef71-4feb-a7a8-321338251d34/problem-with-repeating-section-content-control-in-word-2013?forum=worddev

    The problem discussed there was really about the non-appearance of extra rows for each repeating Element in the relevant part of the XML, and the solution to that does appear to be to as discussed.

     a. insert enough XML to allow you to make the correct mappings (in your case, perhaps the XML for a single employee and a single experience)

     b. make the mappings

     c. replace the body of the XML part  using the XML you really want to use, e.g. if your XML is in string s

    cxn = theCXP.SelectSingleNode("/").LastChild;
    theCXP.SelectSingleNode("/").ReplaceChildSubTree(s, cxn)

    (which is the syntax suggested by Peter A Johannson)

    However, tests here suggest that introducing your own namespace suffix may also be causing problems, although again, I do not know why (perhaps Word does not lie to have multiple suffixes associated with the same namespace URI). In your case I think you can either

     a. guess that you should use ns0 rather than ns, and use

    string xPathName = "ns0:employees/ns0:employee/ns0:employeeName";
                this.ccEmployeeName.XMLMapping.SetMapping(xPathName, , employeeXMLPart);
    

    You would need to get the syntax right for the missing parameter in C#.

    Or you could iterate through the 

    employeeXMLPart.NamespaceManager

    collection to find the member that matches your namespace URI

    'http://schemas.mydomain.com/qms'

    and extract the prefix (should be "ns0" in this case), then build your XPath paths using that.

    Just as an observation, Word generally generates the most specific XPath it can, so it would typically use

    /ns0:employees/ns0:employee[1]/ns0:employeeName[1]

    (with the initial "/" and element indexes).

    Finally, I am not sure about this mapping:

              string xPathExpRep = "ns:employees/ns:employee/ns:experience[*]";
                this.ccExpRep.XMLMapping.SetMapping(xPathExpRep, prefix, employeeXMLPart);
     

    I think Word would use something more like

    string xPathExpRep = "/ns0:employees/ns0:employee[0]/ns0:experience";


    Peter Jamieson

    Friday, June 1, 2018 12:14 PM