Utility to generate Word documents from templates using Visual Studio 2010 and Open Xml 2.0 SDK


This utility generates Word documents from templates using Content controls. The utility source code is available for download at http://worddocgenerator.codeplex.com/. It has been created in Visual Studio 2010 and uses Open Xml 2.0 SDK which can be downloaded from http://www.microsoft.com/download/en/details.aspx?id=5124.

The next parts in this series are

  • In Part 2 I have discussed about
    • List of functionalities that can be achieved using the utility/source code
    • Description regarding Samples provided with utility
    • New samples added in this update
  • In Part 3 I have explained one of the way to “Refresh the document from within the document(e.g. right click on document and click Refresh) using document-level projects for Word 2007 and Word 2010“

The purpose of creating this utility was to use the Open Xml 2.0 SDK to generate Word documents based on predefined templates using minimum code changes. These documents can either be refreshable or non- refreshable. I’ll explain this difference later. Also there is no dependency that Word should be installed.

A few samples for generating Word 2010 documents have been provided. The screenshots below display the sample template and the document generated out of this template using this utility. 

Word 2010 Template –> Generated Document:

3

 

Word 2010 Template –> Generated Document –> Refreshed Document:

1

 

Document Refresh from within Word –> Refreshed Document:

2

 

Template Design:

The sample templates are inside “WordDocumentGenerator.Client\Sample Templates” folder. A content control as displayed below can have Title and Tag properties.

clip_image003

The logic is to have a tag property of a content control and then populate data using that placeholder. This means every content control inside a Word template will have a different Tag.

As per image above the tag of the content control is “PlaceholderNonRecursiveB”. During document generation we can assign (not mandatory) a unique Id e.g. Guid of a record to make the tag unique e.g. “PlaceholderNonRecursiveB:Guid”. Let’s say that we have an Id and Name field. Thus the Name will be the content of the control and tag will be “PlaceholderNonRecursiveB:Id”. As per Word 2010 the Tag maximum length is 64.

In code we map the tag to the PlaceHolderType enum.

public enum PlaceHolderType
{
        None = 0,
        Recursive = 1,
        NonRecursive = 2,
        Ignore = 3,
        Container = 4    
}
There can be multiple types of PlaceHolders
  • Recursive: This type corresponds to controls where there is 1:N relation between template and data i.e. one example will be repeating a list of Items.
  • Non-Recursive: This type corresponds to controls where there is 1:1 relation between template and data i.e. one example will be showing a User name.
  • Ignore: No action is required for these controls.
  • Container: This type is required only for refreshable documents. We save the container region in CustomXmlPart the first time document is generated from template. Next time onwards we retrieve the container region that was saved and refresh the document. This makes the document self-refreshable.

I’ve named the tags in “Test_Template – 1.docx” as per their PlaceHolder type to make it more clear.

Implementation:

As explained above the Tag property will used to bind data to this content control. The project “WordDocumentGenerator.Library” is the utility library. The project “WordDocumentGenerator.Client” shows how this library can be used to generate documents.

“DocumentGenerator” is the base class that needs to be inherited by the document generators. The sample generators location is “WordDocumentGenerator.Client\Sample Document Generators”.

In order to protect the document I’ve used already created salt and hash. For an implementation where one needs to have document protection enabled for custom passwords one can view http://blogs.msdn.com/b/vsod/archive/2010/04/05/how-to-set-the-editing-restrictions-in-word-using-open-xml-sdk-2-0.aspx

Summary:

The purpose of creating this utility was to use the Open Xml 2.0 SDK to generate Word documents based on predefined templates using minimum code changes. These documents can either be refreshable or non- refreshable. New samples will be added as per feedback.

Comments (29)

  1. Alen says:

    I tried the utility and it works great. When shall I expect some samples on documents generation using Databound Content Controls

  2. Atul.Verma says:

    Thanks Alen for the feedback. I'll update utility for "Document generation using Databound Content Controls" samples in couple of days.

  3. Atul.Verma says:

    The document generation samples using data bound content controls have been added to the utility.

  4. Tarun__Arora says:

    Well Done Atul! This is great stuff, 5+ :-]

    Wondering if you have any samples for generating charts/graphs using Open Office SDK handy?

  5. Atul.Verma says:

    Thanks Tarun. The samples for charts will be added by Wednesday.

  6. Atul.Verma says:

    The samples for charts (Scatter & Line) have been added.

  7. Tarun__Arora says:

    Excellent. Great work once again!

    A sample where you programatically remove Content1 (a content control) would also be of great help.

    Cheers.

  8. Atul.Verma says:

    In case you want to remove content control while keeping it's content check RemoveContentControlsAndKeepContents method of OpenXmlHelper class.

  9. Tarun__Arora says:

    Thanks Atul, now that you mentioned. I guess i'll use this after the document generation to remove all the content tags (the user doesn't really need to see them). What would the behaviour be if i chose to remove content control vendorDetailRow would this remove the sub tag VendorId and VendorName recursively in the table?

    The question I intended to ask in my previous comment was – My document template has 3 sections, the user decides which sections they would like to populate, at the end of the generation I would like to remove all other sections from the template that the user did not opt to populate. So, i thought, the easiest way would be to wrap each section in a content control and in the end remove the content control that the user did not opt to populate. The question remains, is there an easy method to remove the content control with the content?

    Thanks

  10. Tarun__Arora says:

    I think i have answered the 2nd part of the question. Please feel free to correct me if there is a better way of doing this. Once again, the sample you have put out there is more than enough to educate just about any one on this subject. Great work!

    using (var wordDocument = WordprocessingDocument.Open(ms, true))

                   {

                       wordDocument.ChangeDocumentType(WordprocessingDocumentType.Document);

                       var mainDocumentPart = wordDocument.MainDocumentPart;

                       var document = mainDocumentPart.Document;

                       var cc =

                           document.Body.Descendants<SdtElement>().FirstOrDefault(

                               r => r.SdtProperties.GetFirstChild<Tag>().Val.HasValue &&

                                    r.SdtProperties.GetFirstChild<Tag>().Val.Value == "myContentControlName");

                       mainDocumentPart.Document.Body.RemoveChild(cc);

                       document.Save();

                   }

  11. Tarun__Arora says:

    Job Done => And taking your advice on how to remove the content controls – I have put together the sample below;

    using (var wordDocument = WordprocessingDocument.Open(ms, true))

                   {

                       wordDocument.ChangeDocumentType(WordprocessingDocumentType.Document);

                       var mainDocumentPart = wordDocument.MainDocumentPart;

                       var document = mainDocumentPart.Document;

                       // Clean up: The user will appreciate a clean document!

                       var helper = new OpenXmlHelper(@"schemas.worddocumentgenerator.com/DocumentGeneration");

                       helper.RemoveContentControlsAndKeepContents(document);

                       document.Save();

                   }

  12. Atul.Verma says:

    The method I shared removes the control while keeping its contents. If whole control needs to be removed your approach is fine. However a better way will be

    if (cc.Parent != null)

    {

    cc.Remove();

    }

    This way one need not to know about Parent i.e. Document in your case.

    Regarding first part content controls are just placeholders. I write the container controls to CustomXmlPart so that I can refresh the document. If only one time generation is there one can choose to remove all controls while keeping text. The Final report sample does this. All depends how much customization and control is required.

    I write document metadata so as to validate the document if required. An example will be providing an upload document functionality where documents which have metadata as XYZ are valid for upload.

    I'll try to cover this functionality and partial document editing in next update. Do rate the CodePlex project. Thanks.

  13. Shiva says:

    Hi Atul,

    i just wanted to know how do i add content controls dynamically to a word document  template.

    Thanks,

    Shiva.

  14. Atul.Verma says:

    @Shiva – You can do that by analyzing the code that Open Xml SDK 2.0 Productivity tool generates e.g. Have a word document which has content controls. Open the document in Open Xml 2.0 Productivity tool and you can view/copy the logic/code.

  15. Jarry says:

    i just wanted to know how do i add Img-content controls

    thanks

  16. Atul.Verma says:

    @Jarry … Images inside content controls has already beel discussed @ worddocgenerator.codeplex.com/…/348810 . Thanks.

  17. Jarry says:

    i want to know how do i add loop-different-img in a loop,

    this question is very important for me, how to use img-control,

    Looking forward to your reply

    thanks!

  18. Atul.Verma says:

    @Jarry … This thread is quite close to what you are looking for

    openxmldeveloper.org/…/131867.aspx

    Let me know if further information is required. Thanks.

  19. Cristina says:

    What version of .NET is needed?

  20. Atul.Verma says:

    .NET 3.5 or .NET 4.0

  21. Adrian says:

    Hi Atul,

    I have a question that if I want to read a word as source, retrieve some data put in another word. Is that a good idea to use XML to save those data? If yes, do you have any code can share to me how to retrieve data from word to XML and then i can user this utility to generate a word document?

    Adrian

  22. Thanks it's saved my lot of time.

    May be I'm asking more, is it possible to update picture in the picture control content?

    Thanks in advance.

  23. Atul.Verma says:

    Yes you can do that. I couldn't get time to add that feature to this solution as I was busy with a Technical review of  book on Kinect for Windows SDK http://www.packtpub.com/kinect-for-windows-software-development-kit-programming-guide/book. You can find the discussion helpful regarding picture content control

    worddocgenerator.codeplex.com/…/348810

  24. Atul.Verma says:

    @Adrian … Retrieve a data from Word as source? What exactly are you trying to retrieve i.e. is it text, paragraph or some content control's text. Is the source Word document already a template having content controls?

    If the source has content controls then we can copy the content control's text from once document to other easily. Otherwise there are many things that needs to be taken care. Let me know the details. Thanks.

  25. Varun Sharma says:

    Hi Atul,

    Here is the scenario:

    I have to generate a word doc from my dot net application which will contain as many tables as there are records in the database – which means if I have 100 records in the database, I have to create 100 such tables in the word doc where each table will contain details about each record of the database.

    So there is no way I can pre-define the number of content templates in the word doc because I would never know how many tables I need to create. What is the suggested approach in this case?

    Thanks!

  26. Kevin says:

    hi Atul, great work! are you still improving this util? what's the state of OpenXML? is there going to be SDK v3? do u know?

    Thanks

  27. rk says:

    how to run this could you please tell me.

  28. Kevin says:

    hi Atul, great work! are you still improving this util? what's the state of OpenXML? is there going to be SDK v3? do u know?

    Thanks

  29. Tim says:

    Hi Atul,

    Looks like great work!  Just getting started with the project code downloaded from CodePlex, but running into the following error when I try to open the project:

    ..WordDocumentGenerator.WordRefreshableDocumentAddinWordDocumentGenerator.WordRefreshableDocumentAddin.csproj : error  : Cannot create the project because the application associated with this project type is not installed on this computer. You must install the Microsoft Office application that is associated with this project type.

    I am using VS 2010, and have Open XML SDK 2.0 installed.  Am I missing something?

    Thanks,

    — Tim