Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add an existing CustomProperty does not update the document

Tags:

c#

.net

docx

I am actually using the library "DocX" to generate Word document (2007+) from .Net. What is good is that it can use "docx" template to recreate or update a document.

Problem: 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 CTRL+F9

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

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

Project and samples are available at : http://docx.codeplex.com/

like image 338
Nordes Avatar asked Nov 09 '11 08:11

Nordes


1 Answers

The problem is when we use MS Word (I use the 2010 version) and then we modify the template and save it. It changes what the document contains.

Here's what we have when we first generate the template using DocX:

<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>

And when we edit with Word (add a break line or some text) and we save it, it rewrite the fldSimple to something like that:

<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>

Instead of waiting someone to fix the issue, I simply tried to do a first draft of implementation. I actually modified the method UpdateCustomPropertyValue(...). I actually added the code of the first foreach. The second foreach was already there and it apply to document created from DocX.

 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));
                }
            }
}
like image 184
Nordes Avatar answered Oct 21 '22 06:10

Nordes