The Open XML SDK 2.0 for Microsoft Office

Along with the introduction of Microsoft Dynamics GP 2010 Word Templates came a little known software development kit: Open XML SDK 2.0.

Open XML is an open ECMA 376 standard and is also approved as the ISO/IEC 29500 standard that defines a set of XML schemas for representing spreadsheets, charts, presentations, and word processing documents. Microsoft Office 2007 and Microsoft Office 2010 all use Open XML as the default file format for rendering spreadsheets, documents, and presentations.

The Open XML file formats are useful for developers because they use an open standard and are based on well-known technologies: ZIP and XML.

The Open XML SDK 2.0 for Microsoft Office is built on top of the System.IO.Packaging API and provides strongly typed part classes to manipulate Open XML documents. The SDK also uses the .NET Framework Language-Integrated Query (LINQ) technology to provide strongly typed object access to the XML content inside the parts of Open XML documents.

In the case of Microsoft Dynamics GP 2010, the assembly enables developers to easily read-from and write-to Microsoft Word documents using their favorite language in Microsoft Visual Studio. The following image depicts the assembly in the Global Assembly Cache (GAC).


You can find more examples and a cool tool available at the Open XML SDK 2.0 download site. Using this tool, Rob Wagner was able to compare the original template shipped with GP and the modified version I created, when troubleshooting some issues I was having when adding a new section to the original template – see Debugging Microsoft Dynamics GP 2010 Word Templates.

Using the Open XML to work with Word documents

The following C# console application demonstrates how to use OpenXML to add a new table to the end of a document (similar to the text added in the “terms and conditions” article).

//---------------------------------------------------------------------
//  This file is a Microsoft Dynamics GP Business Intelligence Code Sample.
// 
//  Copyright (C) Microsoft Corporation.  All rights reserved.
// 
//This source code is intended only as a supplement to Microsoft
//Development Tools and/or on-line documentation.  See these other
//materials for detailed information regarding Microsoft code samples.
// 
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using wp = DocumentFormat.OpenXml.Wordprocessing;

namespace NewTableAdHocText
{
    class Program
    {
        public bool AddTableAdHocText(string WordDocPath, string TextPath)
        {
            try
            {
                //read the ad-hoc text and store it for later
                StreamReader srStreamReader = new StreamReader(TextPath);
                String sString = srStreamReader.ReadToEnd();

                //open the word document and it's main document part
                using (WordprocessingDocument wdWordDoc = WordprocessingDocument.Open(WordDocPath, true))
                {
                    MainDocumentPart mdpWordDocMainPart = wdWordDoc.MainDocumentPart;
                    //create then add a new paragraph
                    wp.Paragraph wppParagraph = new wp.Paragraph(new wp.ParagraphProperties(new wp.ParagraphStyleId() { Val = "NoSpacing" }));
                    mdpWordDocMainPart.Document.Body.Append(wppParagraph);
                    //create new markup for the table
                    Table wppTable = new Table(
                        new TableProperties(
                            new TableStyle() { Val = "TableGrid" },
                            new TableWidth() { Width = "0", Type = TableWidthUnitValues.Auto },
                        new wp.TableBorders(
                            new TopBorder() { Val = BorderValues.None },
                            new LeftBorder() { Val = BorderValues.None },
                            new BottomBorder() { Val = BorderValues.None },
                            new RightBorder() { Val = BorderValues.None, },
                            new InsideHorizontalBorder() { Val = wp.BorderValues.None },
                            new InsideVerticalBorder() { Val = wp.BorderValues.None }),
                        new TableLook() { Val = "04A0", FirstRow = true, LastRow = false, FirstColumn = true, LastColumn = false, NoHorizontalBand = false, NoVerticalBand = true }),
                        new TableGrid(
                            new GridColumn() { Width = "11016" }),
                        new TableRow(
                            new TableCell(
                                new TableCellProperties(
                                    new TableCellWidth() { Width = "11016", Type = wp.TableWidthUnitValues.Dxa }),
                                    new Paragraph(
                                        new ParagraphProperties(
                                            new ParagraphStyleId() { Val = "NoSpacing" },
                                            new KeepLines(),
                                            new PageBreakBefore(),
                                            new ParagraphMarkRunProperties(
                                                new RunFonts() { Ascii = "Trebuchet MS", HighAnsi = "Trebuchet MS" },
                                                new FontSize() { Val = "17" },
                                                new FontSizeComplexScript() { Val = "17" }),
                                            new LastRenderedPageBreak(),
                                            new Run(
                                                new RunProperties(
                                                    new RunFonts() { Ascii = "Trebuchet MS", HighAnsi = "Trebuchet MS" },
                                                    new FontSize() { Val = "17" },
                                                    new FontSizeComplexScript() { Val = "17" },
                                                new Text(sString)))))))); //text for the run comes from the adhoc text document
                    //add then save the table to the document
                    mdpWordDocMainPart.Document.Body.Append(wppTable);
                    mdpWordDocMainPart.Document.Save();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.InnerException);
                return false; //failure
            }
            return true;
        }

        static void Main(string[] args)
        {
            new Program().AddTableAdHocText(args[0], args[1]);
        }
    }
}

The above example shows how simple it is to:

1) Crack open a word document.
2) Append a table to the end with specific properties.
3) Insert some ad-hoc text into the table.

The major advantage is the strongly typed interface to office document. You don’t need COM interoperability to manipulate any document.

You can download the sample project at the bottom of this article.

Downloads

NewTableAdhocText.zip - C# command line application to demonstrate adding a table at the end of a Word Document.

Until next post!

MG.-
Mariano Gomez, MVP
Maximum Global Business, LLC
http://www.maximumglobalbusiness.com/

Comments

Unknown said…
Your article is really cool, there's a lot of information you shared to us and I really appreciate it coz I'm not that tech savvy. Looking forward to your other tips and information.

http://www.filecure.com
Mariano Gomez said…
Steven,

Much thanks for the comment. Keep up the readership.

MG.-
Anonymous said…
Is there a way to create word templated for Project Accounting invoices ? The template maintenance window is not showing any Project Accounting reports for selection. the report writer documentation has mentioned that the report has to be template-enabled. How do we do that?

Thanks
gold138@live.com
Mariano Gomez said…
Gold138,

You can make a report that is not template-enabled become so, by using Microsoft Dexterity. In other words, this would require a customization.

Take a look at the Microsoft Dexterity Integration Guide (IG.PDF) for further information on building report templates for Report Writer reports.

MG.-

Popular posts from this blog

DBMS: 12 Microsoft Dynamics GP: 0 error when updating to Microsoft Dynamics GP 2013 R2

Cannot insert the value NULL into column 'CONTACT' error when clicking on Items List in Navigation Pane

#PowerApps: Numeric Up/Down control with persisted button press event using components