Sep 16

What is a document property you may ask? These are the common tags associated to a document like who is the author, last saved time, title, the total number of pages …
Here is a screenshot of the panel you should be familiar now (if not, follow this Howto to know how to display it).

Sure you know *now * what "is" a document properties but what can I do with them? Maybe you can add some hidden information about a generated document. Or you can define some to-replace content placeholders.
Personally when I need to generate some documents from a template, I used them as some "static" placeholders as I know these values will not change at each new document.

To use them, go to Insert, QuickParts then Fields… Click DocProperty on the left menu and select the property of your choice. You will obtain something like this:

Right-click on a field to toggle its value or code:

 

Add property using Word

To add the field, follow the same steps as for a classic field:

 

Add property using OpenXml

This is easier to find, they are stored in CustomFilePropertiesPart.

 

To display the property name and value, use this snippet:

foreach (var p in package.CustomFilePropertiesPart.Properties.Elements<op.Property>()) 
{ 
    Console.WriteLine("{0}: {1}", p.Name, p.InnerText); 
}
 

So to add a string value, use:

package.CustomFilePropertiesPart.Properties.Append(new op.Property( 
    new vt.VTLPWSTR("Some Value") 
) { FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", PropertyId = 2, Name = "MyProperty" });
 

Hu wait a minute?! Why PropertyId=2? The Id is a required field and should be unique. You can assign yourself some hard-coded value but that's absolutely not safe. We will loop through the Property to retrieve the highest Id.

int nextPropertyId = 2; // 2 is the minimum ID set by MS Office. Don't have a clue why they don't start at 0. 

foreach (var p in package.CustomFilePropertiesPart.Properties.Elements<op.Property>()) 
{ 
    if (p.PropertyId.Value > nextPropertyId) nextPropertyId = p.PropertyId; 
} 
if(nextPropertyId > 2) nextPropertyId++;

Finally, we will update the body part (+ header and footer). But that have already been solved on this forum post.

XmlNamespaceManager nsMgr = new XmlNamespaceManager(new NameTable()); 
nsMgr.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"); 
nsMgr.AddNamespace("op", "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"); 

XmlDocument document = new XmlDocument(); 
document.Load(mainPart.GetStream(FileMode.Open, FileAccess.ReadWrite)); 

XmlNodeList nodes = xmlDocument.SelectNodes( 
    String.Format("//w:fldSimple[@w:instr=' DOCPROPERTY {0} \\* MERGEFORMAT ']", property.Key), nsMgr); 
if (nodes.Count > 0) 
{ 
    foreach (XmlNode n in nodes) 
    { 
        XmlNodeList textNodes = n.SelectNodes("w:r//w:t", nsMgr); 
        if (textNodes.Count > 0) 
        { 
            textNodes[0].InnerText = property.Value; 
            for (int j = 1; j < textNodes.Count; j++) 
                textNodes[j].RemoveAll(); 
        } 
    }
}

The complete source contains more code (ensure OpenXmlPart exists, disposing resource, …) and accepts a collection bag of custom properties:
UpdateDocumentProperties(WordprocessingDocument package, IDictionary<String, String> properties)

Download code at: OpenXml Demo.zip (5.21 kb)

Tags:

Comments

Bodum

Posted on Wednesday, 18 November 2009 05:17

Personally when I need to generate some documents from a template, I used them as placeholders

fuel pump

Posted on Sunday, 9 May 2010 15:19

Thanks for post. It’s really informative stuff.
I really like to read.Hope to learn a lot and have a nice experience here! my best regards guys!

unfinished wood boxes

Posted on Thursday, 20 May 2010 13:16

I had no idea that you could add properties in this way.

CFD Trading

Posted on Sunday, 30 May 2010 19:22

Thanks for making my like alot easier. Great blog post

Comments are closed