This project has moved and is read-only. For the latest updates, please go here.

Add an existing CustomProperty does not update the document

Nov 9, 2011 at 8:00 AM
Edited Nov 9, 2011 at 9:29 AM

Hello,

When I "AddCustomProperty(...)" it does not update the word document. I actually need to open it and then select all and push F9. I was wondering if there was a way to auto-update the "custom properties" using the DocX library. 

To reproduce my problem you can do the following steps:

  1. Open the "sample" available on the DocX project.
  2. Run it once (it will create files in the debug\docs\)
  3. Open the invoice template and then add a line (with or without text) and save the file
  4. Re-run the same sample project (it will use the existing template)
  5. Open the invoice result. When you do so, you can see that the table have been created, BUT, all other fields haven't been updated until you select all and then push F9

If anyone has a solution, I would gladly like to see it.

(Note: I don't want the MS Word interop)

Nov 9, 2011 at 8:25 AM
Edited Nov 9, 2011 at 8:42 AM

Note that I am using word 2010

What I see is that when I save, instead of being a "fldSimple" it save it as a "instrText". So when the function UpdateCustomPropertyValue executes, it does not find any "fldSimple".

---

<w:fldSimple w:instr="DOCPROPERTY company_name \* MERGEFORMAT" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:r>
    <w:t>
      <w:rPr>
        <w:b />
        <w:sz w:val="24" />
        <w:szCs w:val="24" />
        <w:color w:val="1F497D" />
      </w:rPr>Company Name</w:t>
  </w:r>
</w:fldSimple>

vs

<w:p w:rsidR="006D64DE" w:rsidRDefault="006B25B1">
            <w:r>
              <w:fldChar w:fldCharType="begin" />
            </w:r>
            <w:r>
              <w:instrText>DOCPROPERTY company_name \* MERGEFORMAT</w:instrText>
            </w:r>
            <w:r>
              <w:fldChar w:fldCharType="separate" />
            </w:r>
            <w:r>
              <w:rPr>
                <w:b />
                <w:color w:val="1F497D" />
                <w:sz w:val="24" />
                <w:szCs w:val="24" />
              </w:rPr>
              <w:t>Company Name</w:t>
            </w:r>
            <w:r>
              <w:rPr>
                <w:b />
                <w:color w:val="1F497D" />
                <w:sz w:val="24" />
                <w:szCs w:val="24" />
              </w:rPr>
              <w:fldChar w:fldCharType="end" />
            </w:r>
          </w:p>

Nov 9, 2011 at 1:36 PM

Maybe not the "most" beautiful solution, but anyway. I downloaded the source and did the following modification in UpdateCustomPropertyValue(...). In facts, I added the "instrText". It may not be elegant, but it is a first draft and the code does not give a "wow" effect ;). I know that I miss some validation for null value

File: DocX.cs

 internal static void UpdateCustomPropertyValue(DocX document, string customPropertyName, string customPropertyValue)
        {
			foreach (XElement e in document.mainDoc.Descendants(XName.Get("instrText", w.NamespaceName))) 
			{
				string attr_value = e.Value.Replace(" ", string.Empty).Trim();
				string match_value = string.Format(@"DOCPROPERTY  {0}  \* MERGEFORMAT", customPropertyName).Replace(" ", string.Empty);

				if (attr_value.Equals(match_value, StringComparison.CurrentCultureIgnoreCase))
				{
					XNode node = e.Parent.NextNode;
					bool found = false;
					while (true)
					{
						if (node.NodeType == XmlNodeType.Element)
						{
							var ele = node as XElement;
							var match = ele.Descendants(XName.Get("t", w.NamespaceName));
							if (match.Count() > 0)
							{
								if (!found)
								{
									match.First().Value = customPropertyValue;
									found = true;
								}
								else
								{
									ele.RemoveNodes();
								}
							}
							else
							{
								match = ele.Descendants(XName.Get("fldChar", w.NamespaceName));
								if (match.Count() > 0)
								{
									var endMatch = match.First().Attribute(XName.Get("fldCharType", w.NamespaceName));
									if (endMatch != null && endMatch.Value == "end")
									{
										break;
									}
								}
							}
						}
						node = node.NextNode;
					}
				}
			}
			 
			foreach (XElement e in document.mainDoc.Descendants(XName.Get("fldSimple", w.NamespaceName))) 
            {
				string attr_value = e.Attribute(XName.Get("instr", w.NamespaceName)).Value.Replace(" ", string.Empty).Trim();
                string match_value = string.Format(@"DOCPROPERTY  {0}  \* MERGEFORMAT", customPropertyName).Replace(" ", string.Empty);

                if (attr_value.Equals(match_value, StringComparison.CurrentCultureIgnoreCase))
                {
                    XElement firstRun = e.Element(w + "r");
                    XElement firstText = firstRun.Element(w + "t");
                    XElement rPr = firstText.Element(w + "rPr");

                    // Delete everything and insert updated text value
                    e.RemoveNodes();

                    XElement t = new XElement(w + "t", rPr, customPropertyValue);
                    Novacode.Text.PreserveSpace(t);
                    e.Add(new XElement(firstRun.Name, firstRun.Attributes(), firstRun.Element(XName.Get("rPr", w.NamespaceName)), t));
                }
            }
}

Nov 14, 2011 at 1:07 PM

Thanks for putting an effort to fix this yourself! Do you think you could prepare the code to be "good enough" to be included in sources for everyone?

Nov 15, 2011 at 12:04 PM
Edited Nov 21, 2011 at 8:25 PM

I will commit it this week (when I will have some free time ;) ). 

Edit: I wanted to open the project with TFS, but I don't have access. So I'll try to send the "patch" through codeplex..

Dec 30, 2011 at 5:36 PM
Edited Dec 30, 2011 at 5:55 PM

Have this fix been applied already to the sources?


Edit: I checked the sources of newest version and it still has some old code. 

Dec 31, 2011 at 5:42 PM

No, it hasn't been added to the source code. I cannot access the repository with TFS (I don't have an account valid to add stuff to this project). I saw that I can only add a "kind" of patch by submitting a patch via the web.  

Jan 2, 2012 at 1:31 PM

Hi Nordes,

the next task on my list was to fix CustomProperties.
I have actually been working on it for the past hour... then I happened to read this thread because it was releated.
I was delighted when I found your code above. I have tested it with all of my use cases and it works perfectly.

I would be delighted if you would commit your changes to the source tree.
I have just made you a developer of DocX.
Welcome to the team.

Kind regards,
Cathal

P.S. The next step is to support Headers and Footers. I will start looking into this.

Jan 2, 2012 at 2:04 PM

Code modified slightly to support Headers and Footers

internal static void UpdateCustomPropertyValue(DocX document, string customPropertyName, string customPropertyValue)
{
    // A list of documents, which will contain, The Main Document and if they exist: header1, header2, header3, footer1, footer2, footer3.
    List<XElement> documents = new List<XElement>{ document.mainDoc.Root };

    // Check if each header exists and add if if so.
    #region Headers
    Headers headers = document.Headers;
    if (headers.first != null)
        documents.Add(headers.first.Xml);
    if (headers.odd != null)
        documents.Add(headers.odd.Xml);
    if (headers.even != null)
        documents.Add(headers.even.Xml); 
    #endregion

    // Check if each footer exists and add if if so.
    #region Footers
    Footers footers = document.Footers;
    if (footers.first != null)
        documents.Add(footers.first.Xml);
    if (footers.odd != null)
        documents.Add(footers.odd.Xml);
    if (footers.even != null)
        documents.Add(footers.even.Xml);
    #endregion

    // Process each document in the list.
    foreach (XElement doc in documents)
    {
        foreach (XElement e in doc.Descendants(XName.Get("instrText", w.NamespaceName)))
        {
            string attr_value = e.Value.Replace(" ", string.Empty).Trim();
            string match_value = string.Format(@"DOCPROPERTY  {0}  \* MERGEFORMAT", customPropertyName).Replace(" ", string.Empty);

            if (attr_value.Equals(match_value, StringComparison.CurrentCultureIgnoreCase))
            {
                XNode node = e.Parent.NextNode;
                bool found = false;
                while (true)
                {
                    if (node.NodeType == XmlNodeType.Element)
                    {
                        var ele = node as XElement;
                        var match = ele.Descendants(XName.Get("t", w.NamespaceName));
                        if (match.Count() > 0)
                        {
                            if (!found)
                            {
                                match.First().Value = customPropertyValue;
                                found = true;
                            }
                            else
                            {
                                ele.RemoveNodes();
                            }
                        }
                        else
                        {
                            match = ele.Descendants(XName.Get("fldChar", w.NamespaceName));
                            if (match.Count() > 0)
                            {
                                var endMatch = match.First().Attribute(XName.Get("fldCharType", w.NamespaceName));
                                if (endMatch != null && endMatch.Value == "end")
                                {
                                    break;
                                }
                            }
                        }
                    }
                    node = node.NextNode;
                }
            }
        }

        foreach (XElement e in doc.Descendants(XName.Get("fldSimple", w.NamespaceName)))
        {
            string attr_value = e.Attribute(XName.Get("instr", w.NamespaceName)).Value.Replace(" ", string.Empty).Trim();
            string match_value = string.Format(@"DOCPROPERTY  {0}  \* MERGEFORMAT", customPropertyName).Replace(" ", string.Empty);

            if (attr_value.Equals(match_value, StringComparison.CurrentCultureIgnoreCase))
            {
                XElement firstRun = e.Element(w + "r");
                XElement firstText = firstRun.Element(w + "t");
                XElement rPr = firstText.Element(w + "rPr");

                // Delete everything and insert updated text value
                e.RemoveNodes();

                XElement t = new XElement(w + "t", rPr, customPropertyValue);
                Novacode.Text.PreserveSpace(t);
                e.Add(new XElement(firstRun.Name, firstRun.Attributes(), firstRun.Element(XName.Get("rPr", w.NamespaceName)), t));
            }
        }
    }
}

Jan 2, 2012 at 3:32 PM

Thank you. I Will be back from my holiday next week. So i will be able to commit next tuesday.

Regards,

Nordes

Le 2 janv. 2012 09:04, "coffeycathal" <notifications@codeplex.com> a écrit :

From: coffeycathal

Code modified slightly to support Headers and Footers

internal static void UpdateCustomPropertyValue(DocX document, string customPropertyName, string customPropertyValue)
{
    // A list of documents, which will contain, The Main Document and if they exist: header1, header2, header3, footer1, footer2, footer3.
    List<XElement> documents = new List<XElement>{ document.mainDoc.Root };

    // Check if each header exists and add if if so.
    #region Headers
    Headers headers = document.Headers;
    if (headers.first != null)
        documents.Add(headers.first.Xml);
    if (headers.odd != null)
        documents.Add(headers.odd.Xml);
    if (headers.even != null)
        documents.Add(headers.even.Xml); 
    #endregion

    // Check if each footer exists and add if if so.
    #region Footers
    Footers footers = document.Footers;
    if (footers.first != null)
        documents.Add(footers.first.Xml);
    if (footers.odd != null)
        documents.Add(footers.odd.Xml);
    if (footers.even != null)
        documents.Add(footers.even.Xml);
    #endregion

    // Process each document in the list.
    foreach (XElement doc in documents)
    {
        foreach (XElement e in doc.Descendants(XName.Get("instrText", w.NamespaceName)))
        {
            string attr_value = e.Value.Replace(" ", string.Empty).Trim();
            string match_value = string.Format(@"DOCPROPERTY  {0}  \* MERGEFORMAT", customPropertyName).Replace(" ", string.Empty);

            if (attr_value.Equals(match_value, StringComparison.CurrentCultureIgnoreCase))
            {
                XNode node = e.Parent.NextNode;
                bool found = false;
                while (true)
                {
                    if (node.NodeType == XmlNodeType.Element)
                    {
                        var ele = node as XElement;
                        var match = ele.Descendants(XName.Get("t", w.NamespaceName));
                        if (match.Count() > 0)
                        {
                            if (!found)
                            {
                                match.First().Value = customPropertyValue;
                                found = true;
                            }
                            else
                            {
                                ele.RemoveNodes();
                            }
                        }
                        else
                        {
                            match = ele.Descendants(XName.Get("fldChar", w.NamespaceName));
                            if (match.Count() > 0)
                            {
                                var endMatch = match.First().Attribute(XName.Get("fldCharType", w.NamespaceName));
                                if (endMatch != null && endMatch.Value == "end")
                                {
                                    break;
                                }
                            }
                        }
                    }
                    node = node.NextNode;
                }
            }
        }

        foreach (XElement e in doc.Descendants(XName.Get("fldSimple", w.NamespaceName)))
        {
            string attr_value = e.Attribute(XName.Get("instr", w.NamespaceName)).Value.Replace(" ", string.Empty).Trim();
            string match_value = string.Format(@"DOCPROPERTY  {0}  \* MERGEFORMAT", customPropertyName).Replace(" ", string.Empty);

            if (attr_value.Equals(match_value, StringComparison.CurrentCultureIgnoreCase))
            {
                XElement firstRun = e.Element(w + "r");
                XElement firstText = firstRun.Element(w + "t");
                XElement rPr = firstText.Element(w + "rPr");

                // Delete everything and insert updated text value
                e.RemoveNodes();

                XElement t = new XElement(w + "t", rPr, customPropertyValue);
                Novacode.Text.PreserveSpace(t);
                e.Add(new XElement(firstRun.Name, firstRun.Attributes(), firstRun.Element(XName.Get("rPr", w.NamespaceName)), t));
            }
        }
    }
}

Read the full discussion online.

To add a post to this discussion, reply to this email (DocX@discussions.codeplex.com)

To start a new discussion for this project, email DocX@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com

Jan 10, 2012 at 3:14 PM

Norde how is it going ? Can you update sources ? :)

Jan 10, 2012 at 3:18 PM

Hello,

Like I said the other day, I was on vacation (I just came back yesterday night in France from Canada and finally have access to my computer). So tonight I will check-in the stuff.

Regards,

Jan 10, 2012 at 8:46 PM

Fixed in changeset 73067 

Oct 29, 2012 at 1:15 PM

It seems not to work with IncludeText property. Can you provide a fix for that? Thanks

Feb 24, 2016 at 4:53 PM
I have problem with my custom property name contains spaces. Word automatically put double quote around the name and thus the matching fails. I've modified the code to accommodate this problem below. I only have office 2010 so I am not sure if this would create problem for other versions. If any one of the developers has permission to check in please test that and commit in the source. Thanks
  internal static void UpdateCustomPropertyValue(DocX document, string customPropertyName, string customPropertyValue)
        {
            // A list of documents, which will contain, The Main Document and if they exist: header1, header2, header3, footer1, footer2, footer3.
            List<XElement> documents = new List<XElement> { document.mainDoc.Root };

            // Check if each header exists and add if if so.
            #region Headers
            Headers headers = document.Headers;
            if (headers.first != null)
                documents.Add(headers.first.Xml);
            if (headers.odd != null)
                documents.Add(headers.odd.Xml);
            if (headers.even != null)
                documents.Add(headers.even.Xml);
            #endregion

            // Check if each footer exists and add if if so.
            #region Footers
            Footers footers = document.Footers;
            if (footers.first != null)
                documents.Add(footers.first.Xml);
            if (footers.odd != null)
                documents.Add(footers.odd.Xml);
            if (footers.even != null)
                documents.Add(footers.even.Xml);
            #endregion

            // Process each document in the list.
            foreach (XElement doc in documents)
            {
                #region Word 2010+
                foreach (XElement e in doc.Descendants(XName.Get("instrText", w.NamespaceName)))
                {
                  
                    var matchCustomPropertyName = customPropertyName;
                    if (customPropertyName.Contains(" ")) matchCustomPropertyName = "\"" + customPropertyName + "\"";
                    string attr_value = e.Value.Replace(" ", string.Empty).Trim();
                    string match_value = string.Format(@"DOCPROPERTY  {0}  \* MERGEFORMAT", matchCustomPropertyName).Replace(" ", string.Empty);

                    if (attr_value.Equals(match_value, StringComparison.CurrentCultureIgnoreCase))
                    {
                        XNode node = e.Parent.NextNode;
                        bool found = false;
                        while (true)
                        {
                            if (node.NodeType == XmlNodeType.Element)
                            {
                                var ele = node as XElement;
                                var match = ele.Descendants(XName.Get("t", w.NamespaceName));
                                if (match.Count() > 0)
                                {
                                    if (!found)
                                    {
                                        match.First().Value = customPropertyValue;
                                        found = true;
                                    }
                                    else
                                    {
                                        ele.RemoveNodes();
                                    }
                                }
                                else
                                {
                                    match = ele.Descendants(XName.Get("fldChar", w.NamespaceName));
                                    if (match.Count() > 0)
                                    {
                                        var endMatch = match.First().Attribute(XName.Get("fldCharType", w.NamespaceName));
                                        if (endMatch != null && endMatch.Value == "end")
                                        {
                                            break;
                                        }
                                    }
                                }
                            }
                            node = node.NextNode;
                        }
                    }
                }
                #endregion

                #region < Word 2010
                foreach (XElement e in doc.Descendants(XName.Get("fldSimple", w.NamespaceName)))
                {
                 
                    var matchCustomPropertyName = customPropertyName;
                    if (customPropertyName.Contains(" ")) matchCustomPropertyName = "\"" + customPropertyName + "\"";
                    string attr_value = e.Attribute(XName.Get("instr", w.NamespaceName)).Value.Replace(" ", string.Empty).Trim();
                    string match_value = string.Format(@"DOCPROPERTY  {0}  \* MERGEFORMAT", matchCustomPropertyName).Replace(" ", string.Empty);

                    if (attr_value.Equals(match_value, StringComparison.CurrentCultureIgnoreCase))
                    {
                        XElement firstRun = e.Element(w + "r");
                        XElement firstText = firstRun.Element(w + "t");
                        XElement rPr = firstText.Element(w + "rPr");

                        // Delete everything and insert updated text value
                        e.RemoveNodes();

                        XElement t = new XElement(w + "t", rPr, customPropertyValue);
                        Novacode.Text.PreserveSpace(t);
                        e.Add(new XElement(firstRun.Name, firstRun.Attributes(), firstRun.Element(XName.Get("rPr", w.NamespaceName)), t));
                    }
                }
                #endregion
            }
        }