More Enhancements to the Document Template

The next step in building this document generation system is to define a few content controls that allow the template designer to write other necessary C# code for the document generation process.  As an example, the template designer needs to write code that executes when the document generation process starts, i.e. the code that will go in Main.  There is other code the template designer needs to write – details below.

This post is the fourth in a series of blog posts. Here is the complete list: Generating Open XML WordprocessingML Documents Blog Post Series

For this particular approach to building a document generation system, I am planning on the following:

  • The document generation process will first process the document template and generate a new C# program.
  • After generating the new program, the document generation process will compile the new program, and then run it, which will then generate all documents for this document generation run.
  • There are, of course, pros and cons to this approach. I’ll mention some of them as I go along.

The generated code will be a combination of:

  • Code to create the Open XML SDK package, and to create an in-memory clone of the template document.
  • LINQ to XML code to update the contents of the main document part in the package. I’ll have more to say about this in the next post. I’m going to write a pure functional transformation from am XML tree to C# code to create the tree. The generated code will use functional construction, not a DOM style approach.
  • Code extracted from the template document and inserted at appropriate places so that it generates the proper WordprocessingML markup. The code to extract the content of the content controls, and include the content control code in the appropriate places. This will be just a few lines of code.

This sounds more complicated than it is. In upcoming posts, I’ll walk through how the code works in detail.  Also, when completed, this example will be super-easy to run.  It will be educational to play around with code in the content controls to generate a wide variety of documents.

I have further refined the C# code that the template designer will write inside of content controls. In addition to that refinement, I have added four more content controls where it doesn’t matter where the template designer places them in the document. These content controls contain code that we need to make the process work – they don’t contain code directly associated with generating content.

The Generated Program Structure

Before discussing the code in the various content controls, I want to discuss the structure of the generated program. There will be a class named Generator, which will be generated in the code generation process. This class will have a couple of properties or fields, and will have one method (beyond the constructor). There will one instance of this class for each generated document.

The code in the Value, Table, and Conditional content controls executes in the context of an instance of the Generator class. Therefore, the code can access instance fields and properties.

New Version of the Template Document

First, I’ll examine the new version of the template document. The titles of the content controls are the same, but the code inside is different from the last post. As before, there are the Value content controls that contain values to be inserted into the document.  I agree with feedback that Svetlin provided – the value derived from this content control will use the formatting of the underlying run or paragraph.  No need to specify a style.

The following code accesses an instance property of the Generator class.  The instance property is Cust.

image

The Table content control looks about the same as in the last post, except that it is now written to access fields in the Generator object:

image

Same thing with Conditional – it can access the Cust instance property.

image

The design of the Ask content control is the same as in the last iteration of this template document:

image

There are four new content controls in this template that enable the template designer to write the necessary code so that the document generation process can generate a C# program that is complete and can compile:

  • Using
  • Classes
  • GeneratorMembers
  • GeneratorMain

The Using content control contains the using statements for the generated program. The generated program may be so simple that it only uses LINQ to XML, as in the example I’m working up. However, it could be more interesting – it might access an OData feed. It might connect to any arbitrary database or web service. It can connect to any data source that you can get to with .NET. Therefore, we need the ability to specify the using statements for the generated program:

image

The Classes content control enables the template designer to define some classes that contain the data that the program reads from the data source.  In this first example, there is a Customer class and an Order class:

image

The GeneratorMembers content control enables the template designer to specify members of the Generator class.  This field contains the customer that a particular Generator object is generating a document for.

image

Last, but not least, there will be the GeneratorMain content control, which contains the code that will go in the Main method in the generated program:

image

This GeneratorMain is expecting to read an XML document that looks like this:

image

And the generated program, after everything is said and done, will look something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace DocumentGenerator
{
    class Customer
    {
        public int CustomerID;
        public string Name;
        public bool IsHighValueCustomer;
    }

    class Order
    {
        public int CustomerID;
        public string ProductDescription;
        public decimal Quantity;
        public DateTime OrderDate;
    }

    class Generator
    {
        Customer Cust;

        void GenerateDocument()
        {
            // This method will be automatically generated during the generation process.
            // There will be a lot of code in this method.
            Console.WriteLine("Generating document for customer {0}", Cust.CustomerID);
        }

        static void Main(string[] args)
        {
            XElement data = XElement.Load("Data.xml");
            var customers = data
                .Elements("Customers")
                .Elements("Customer")
                .Select(c => new Customer() {
                    CustomerID = (int)c.Element("CustomerID"),
                    Name = (string)c.Element("Name"),
                    IsHighValueCustomer = (bool)c.Element("IsHighValueCustomer"),
                });
            var orders = data
                .Elements("Orders")
                .Elements("Order")
                .Select(o => new Order() {
                    CustomerID = (int)o.Element("CustomerID"),
                    ProductDescription = (string)o.Element("ProductDescription"),
                    Quantity = (decimal)o.Element("Quantity"),
                    OrderDate = (DateTime)o.Element("OrderDate"),
                });
            Generator p = new Generator();
            foreach (var customer in customers)
            {
                p.Cust = customer;
                p.GenerateDocument();
            }
        }
    }
}

In the next blog post, I’m going to discuss generating C# code from an XML tree.  This code needs to use functional construction, so that the code generation process can insert queries at all appropriate points in the generated code.  That is going to be fun code to write!!!

Smile

!!!

5 Comments »

  1. Svetlin said,

    February 3, 2011 @ 8:26 pm

    Hi Eric,

    I like the idea of being able to include C# code into the template. I would definitely consider adding it in the next upgrade. This will allow for more complex processing during runtime(there is a requirement already). “Unfortunately”, this would mean a much more skilled person creating these types of templates. So far the focus was on accommodating the “average” Office end-user by providing a drag-and-drop interface. What I will try and do a bit differently, is to hide the “perceived” complexity/clutter of displaying C# code into the document body. I have an idea of how to achieve it, but first have to POC it. I’ll bounce it off you once there is something working. Hope you don’t mind.

    Thanks for the post !

  2. Eric White said,

    February 4, 2011 @ 3:28 pm

    Hi Svetlin,

    You are right, using C# to configure the template means that for all practical purposes, it requires a developer to build the template. Another iteration that would be somewhat easier to use would be to put XML in the content controls. The natural extension of this would be to build a managed add-in for Word with an action pane that makes document configuration much easier. I think in this case, having a document-level customization is more appropriate than an application-level add-in. But this is the step to take *after* refining the template and document generation process until we are satisfied.

    One key disadvantage of putting C# in content controls is that if the developer enters invalid C# code in a content control, the error isn’t reported until the document generation program is generated and compiled. There will be a fairly big disconnect between the error message and the invalid code in the content control. This will be fixed by using XML in content controls and validating the XML in the managed add-in, I think.

    I’m happy to see your POC and have your input!

    -Eric

  3. Umar said,

    September 15, 2011 @ 7:14 pm

    Hi,

    I know this is not the place to post this but I am a bit lost on here and this is my first time using open xml. I used the ooxml sdk tool to reflect the code of my document and I am trying to edit it in vs2010.

    The following code is what i have and it generates perfect except when I open the document I have to hit ALT+F9 to toggle the fields. I am wondering what would I need to add so the end user won’t see the code

    Run run15 = new Run() { RsidRunAddition = “00453F5E” };
    FieldChar fieldChar4 = new FieldChar() { FieldCharType = FieldCharValues.Begin };

    run15.Append(fieldChar4);

    Run run16 = new Run() { RsidRunAddition = “00720C68″ };
    FieldCode fieldCode4 = new FieldCode() { Space = SpaceProcessingModeValues.Preserve };
    fieldCode4.Text = ” IF “;

    run16.Append(fieldCode4);

    SimpleField simpleField5 = new SimpleField() { Instruction = ” MERGEFIELD \”RewriteFlag\” ” };

    Run run17 = new Run() { RsidRunProperties = “00623C0B”, RsidRunAddition = “00394078” };

    RunProperties runProperties6 = new RunProperties();
    NoProof noProof6 = new NoProof();

    runProperties6.Append(noProof6);
    Text fieldCode5 = new Text();
    fieldCode5.Text = “”;

    run17.Append(runProperties6);
    run17.Append(fieldCode5);

    simpleField5.Append(run17);

    Run run18 = new Run() { RsidRunAddition = “00720C68″ };
    Text fieldCode6 = new Text() { Space = SpaceProcessingModeValues.Preserve };
    fieldCode6.Text = ” = 1 \”print this\” \”else print this\””;

    run18.Append(fieldCode6);

    Run run19 = new Run() { RsidRunAddition = “00453F5E” };
    FieldChar fieldChar5 = new FieldChar() { FieldCharType = FieldCharValues.Begin };

    run19.Append(fieldChar5);

    paragraph6.Append(run15);
    paragraph6.Append(run16);
    paragraph6.Append(simpleField5);
    paragraph6.Append(run18);
    paragraph6.Append(run19);

    body.Append(paragraph6);

    Much appreciated
    Umar

  4. Eric White said,

    September 16, 2011 @ 2:14 pm

    Hi Umar,

    First, determine the setting that specifies the mode of seeing field codes (I don’t know what the setting is, but should be easy to find out). Then you can set that setting properly. The way to determine the setting – create a document with field codes, save it, toggle the view, save it as a different name, then use OpenXMLSdk productivity tool to compare the two. Normally I would research this, but am a bit burried right now. 🙂

    -Eric

  5. Shyamdvk said,

    November 7, 2011 @ 9:26 am

    Hi Eric,
    Thanks a lot for for making us to feel about OpenXML. I have quick question. I need a guidance. I have document template named Master, need to generate 7 documents with various section combinations based on the logic. I have able to read the XML and remove/include the section using Content Controls. I have used CustomXML part to map the data into Work Document. I am able to get the output. When i Open the document the content controls are visible to the user, the content is replaced with proper XML values. Here my question is how to hide / “remove” the content control from the display. If we open the generated document I am still able to see the Content Controls with Title and Tag. I do not want the end user to know about it. I would really appreciate your guidance. Already posted my query in “Iterating through Content Controls”. If you can provide solution to both, that would be the great help at this moment.

RSS feed for comments on this post · TrackBack URI

Leave a Comment