Using the SharePoint Foundation 2010 Managed Client Object Model with the Open XML SDK 2.0

Summary:  The Microsoft SharePoint Foundation 2010 managed client object model enables you to write applications that are based on the Microsoft .NET Framework that access SharePoint content from clients without installing code on the server that runs SharePoint Foundation 2010. The Open XML SDK 2.0 for Microsoft Office enables you to write applications to create, change, and query Open XML documents, the default document format for the Microsoft 2007 Office system and Microsoft Office 2010. By using these two technologies together you can write client-side applications that work with Open XML documents that are stored in document libraries.

Applies to: Business Connectivity Services | Office 2010 | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Provided by:  Eric White, Microsoft Corporation

Contents

  • Overview

  • Retrieving Documents from Document Libraries

  • Modifying Documents in Document Libraries

  • Uploading Documents to Document Libraries

  • Creating Documents in Memory and Adding Them to Document Libraries

  • Processing All Documents in a Library

  • Creating Open XML Format Word-Processing Documents from Wiki Libraries

  • Conclusion

  • Additional Resources

Overview

The Microsoft SharePoint Foundation 2010 managed client object model is a set of managed libraries based on the Microsoft .NET Framework that enable you to write code for client computers to work with many of the common objects in SharePoint sites. Programs running on the client can add and remove lists, add, update, and delete list items, change documents in document libraries, create sites, manage permissions of items, and add and remove Web Parts from a page.

Open XML File Formats, the document format of the 2007 Microsoft Office release and the 2010 Office release, is an ISO/IEC standard (IS29500), which describes the internals of word-processing, spreadsheet, and presentation documents. Open XML Format files are stored per the Open Packaging Conventions specification (Part 2 of IS29500). They are basically ZIP files that contain XML parts inside. The Open XML SDK 2.0 for Microsoft Office is a managed library based on the .NET Framework that makes it easy to write programs to create, change, or query Open XML documents. At the end of this article, I list some resources to help you start to work with the Open XML SDK 2.0.

Using these two technologies together enables some interesting scenarios:

  • You can write a client-side application that creates a word-processing document or presentation from content retrieved elsewhere, and automatically uploads this document to a document library on a SharePoint site.

  • You can write an application to query all documents in a document library, extracting information from each document.

  • You can write an application to process all documents in a document library and make the same modifications to each one. One interesting possibility is that you can remove all comments and personal information, and accept all tracked changes in each document in the document library.

  • You can extract the contents of a wiki site, and construct an Open XML document from those contents.

This article builds on the information that is presented in Using the SharePoint Foundation 2010 Managed Client Object Model. For more information about how the SharePoint Foundation 2010 managed client object model works, see that article.

Retrieving Documents from Document Libraries

There are two basic operations when you work with the SharePoint Foundation 2010 managed client object model and Open XML Formats: Retrieving documents and saving documents. The following example retrieves a document from a document library, and prints the text of the first paragraph.

Note

This article uses Microsoft Windows console applications for the sample code. However, you can use the same approach with other application types.

To build the example, you must add references to four assemblies. Two assemblies are necessary for accessing the SharePoint Foundation 2010 managed client object model: Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. Two assemblies are necessary for using the Open XML SDK 2.0: DocumentFormat.OpenXml.dll and WindowsBase.dll. For detailed instructions about how to build a console application that uses the SharePoint Foundation 2010 managed client object model, see Using the SharePoint Foundation 2010 Managed Client Object Model.

To build the examples that are presented in this article, you must also download and install the Open XML SDK 2.0. For detailed instructions about how to build Open XML SDK 2.0 applications, see Welcome to the Open XML SDK 2.0 for Microsoft Office. To download the Open XML SDK 2.0, see Open XML SDK 2.0.

using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
// The following using directive resolves the ambiguity
// between the System.IO.File class and Microsoft.SharePoint.Client.File
// class.
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static private void CopyStream(Stream source, Stream destination)
    {
        byte[] buffer = new byte[32768];
        int bytesRead;
        do
        {
            bytesRead = source.Read(buffer, 0, buffer.Length);
            destination.Write(buffer, 0, bytesRead);
        } while (bytesRead != 0);
    }

    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        List sharedDocumentsList = clientContext.Web.Lists
            .GetByTitle("Shared Documents");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml =
            @"<View>
                <Query>
                  <Where>
                    <Eq>
                      <FieldRef Name='FileLeafRef'/>
                      <Value Type='Text'>Test.docx</Value>
                    </Eq>
                  </Where>
                  <RowLimit>1</RowLimit>
                </Query>
              </View>";
        ListItemCollection listItems = sharedDocumentsList.GetItems(camlQuery);
        clientContext.Load(sharedDocumentsList);
        clientContext.Load(listItems);
        clientContext.ExecuteQuery();
        if (listItems.Count == 1)
        {
            ClientOM.ListItem item = listItems[0];
            Console.WriteLine("FileLeafRef: {0}", item["FileLeafRef"]);
            Console.WriteLine("FileDirRef: {0}", item["FileDirRef"]);
            Console.WriteLine("FileRef: {0}", item["FileRef"]);
            Console.WriteLine("File Type: {0}", item["File_x0020_Type"]);
            FileInformation fileInformation =
                ClientOM.File.OpenBinaryDirect(clientContext, (string)item["FileRef"]);
            using (MemoryStream memoryStream = new MemoryStream())
            {
                CopyStream(fileInformation.Stream, memoryStream);
                using (WordprocessingDocument doc =
                    WordprocessingDocument.Open(memoryStream, false))
                {
                    var firstParagraph = doc
                        .MainDocumentPart
                        .Document
                        .Body
                        .Elements<Paragraph>()
                        .FirstOrDefault();
                    if (firstParagraph != null)
                    {
                        string text = firstParagraph.InnerText;
                        Console.WriteLine(text);
                    }
                    else
                        Console.WriteLine("Document doesn't contain any paragraphs.");
                }
            }
        }
        else
            Console.WriteLine("Document not found.");
    }
}

This example produces output similar to the following.

FileLeafRef: Test.docx
FileDirRef: /Shared Documents
FileRef: /Shared Documents/Test.docx
File Type: docx
This is the text of the first paragraph.

This example prints the fields that you often use when you work with documents in document libraries:

  • FileLeafRef field contains the name of the file.

  • FileDirRef field contains the document library name. If the file is in a folder, the FileDirRef field also contains the folder name.

  • FileRef field contains the full name. This includes the document library name, any folders, and the file name.

The CAML query looks for an item in the document library where the FileLeafRef field has a value of Test.docx, and sets the RowLimit element of the View element to one.

camlQuery.ViewXml =
    @"<View>
        <Query>
          <Where>
            <Eq>
              <FieldRef Name='FileLeafRef'/>
              <Value Type='Text'>Test.docx</Value>
            </Eq>
          </Where>
          <RowLimit>1</RowLimit>
        </Query>
      </View>";

Because the example sets the RowLimit element to one, the query returns a ListItemCollection object that has either zero or one items in it. Therefore, you can test by using listItems.Count == 1 to determine whether the query finds the document.

To retrieve the file, we use the OpenBinaryDirect() method to return an object of type FileInformation.

FileInformation fileInformation =
    ClientOM.File.OpenBinaryDirect(clientContext, (string)item["FileRef"]);

The FileInformation object contains a reference to a stream that you can use to stream the file to our application. However, that stream is not a resizable stream. The Open() method of the WordprocessingDocument class takes a stream as an argument. However, that stream must be a resizable stream. Therefore, you have to create a MemoryStream object, and then copy from the stream returned by the Stream property into the MemoryStream object. If we try to open the WordprocessingDocument object from the stream returned by the Stream property, the Open() method throws an exception, System.IO.IOException, with the message "Cannot open package because FileMode or FileAccess value is not valid for the stream."

You may notice that it was not necessary to call the ExecuteQuery() method before accessing the Stream property. This is because the OpenBinaryDirect() method does not participate in the normal XML/JSON network communication of the client object model. The article Using the SharePoint Foundation 2010 Managed Client Object Model explains how the client part of the client object model bundles requests as XML, the XML is sent to the server, which fulfills the requests, and then sends the response in JSON. If you upload files by using XML and JSON, the client object model converts the binary Open XML formatted document to Base-64 encoding. However, using this encoding is less efficient than directly sending the binary data. We recommend that, when it is possible, to send binary data directly. This results in a more efficient application and less network traffic.

After you have a MemoryStream object, which is resizable if created by using the constructor that takes no parameters, that you open the document by using the the Open() method and process it as usual.

Warning

The MemoryStream class contains a constructor that takes a byte array, which is then used as the backing store for the stream. However, that constructor returns a memory stream that is not resizable. Therefore, you can never use that constructor to create a memory stream for use with the Open XML API.

Modifying Documents in Document Libraries

You can change the previous example so that it alters the document, and puts the altered document back into a document library.

using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static private void CopyStream(Stream source, Stream destination)
    {
        byte[] buffer = new byte[32768];
        int bytesRead;
        do
        {
            bytesRead = source.Read(buffer, 0, buffer.Length);
            destination.Write(buffer, 0, bytesRead);
        } while (bytesRead != 0);
    }

    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        List sharedDocumentsList = clientContext.Web.Lists
            .GetByTitle("Shared Documents");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml =
            @"<View>
                <Query>
                  <Where>
                    <Eq>
                      <FieldRef Name='FileLeafRef'/>
                      <Value Type='Text'>Test.docx</Value>
                    </Eq>
                  </Where>
                  <RowLimit>1</RowLimit>
                </Query>
              </View>";
        ListItemCollection listItems =
            sharedDocumentsList.GetItems(camlQuery);
        clientContext.Load(sharedDocumentsList);
        clientContext.Load(listItems);
        clientContext.ExecuteQuery();
        if (listItems.Count == 1)
        {
            ClientOM.ListItem item = listItems[0];
            Console.WriteLine("FileLeafRef: {0}", item["FileLeafRef"]);
            Console.WriteLine("FileDirRef: {0}", item["FileDirRef"]);
            Console.WriteLine("FileRef: {0}", item["FileRef"]);
            Console.WriteLine("File Type: {0}", item["File_x0020_Type"]);
            FileInformation fileInformation =
                ClientOM.File.OpenBinaryDirect(clientContext,
                (string)item["FileRef"]);
            using (MemoryStream memoryStream = new MemoryStream())
            {
                CopyStream(fileInformation.Stream, memoryStream);
                using (WordprocessingDocument doc =
                    WordprocessingDocument.Open(memoryStream, true))
                {
                    // Insert a new paragraph at the beginning of the
                    //document.
                    doc.MainDocumentPart.Document.Body.InsertAt(new Paragraph(new Run(new Text("Newly inserted paragraph."))), 0);
                }
                // Seek to beginning before writing to the SharePoint server.
                memoryStream.Seek(0, SeekOrigin.Begin);
                // SaveBinaryDirect replaces the document on the SharePoint server.
                ClientOM.File.SaveBinaryDirect(clientContext,(string)item["FileRef"], memoryStream, true);
            }
        }
        else
            Console.WriteLine("Document not found.");
    }
}

Notice that this example passed true to the Open() method so you can change the document. After you open the document by using the the Open() method, you can modify the document as usual.

After exiting the scope created by the statement that opens the document by calling the Open() method, the MemoryStream object contains the modified document. Before you can stream it back to the server, seek to the beginning of the stream. Then, you call the SaveBinaryDirect() method, which puts the altered document back into the document library.

Uploading Documents to Document Libraries

The next operation is uploading existing documents to a document library. You can upload a document by calling the SaveBinaryDirect() method, passing the server-relative URL, and a stream. The following example uploads a new document to the Shared Documents folder of a SharePoint site.

using System;
using System.IO;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
// The following directive avoids ambiguity between the
// System.IO.File class and Microsoft.SharePoint.Client.File class.
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        using (FileStream fileStream =
            new FileStream("NewDocument.docx", FileMode.Open))
            ClientOM.File.SaveBinaryDirect(clientContext,
                "/Shared Documents/NewDocument.docx", fileStream, true);
    }
}

Creating Documents in Memory and Adding Them to Document Libraries

There are two common ways to create documents by using the Open XML Formats API:

  • Use an existing document as a template, open it, modify it in some desired way, and then save it where you want it.

  • Create a document. Often, you do this with the help of the DocumentReflector tool that is included with the Open XML SDK 2.0 for Microsoft Office. This tool enables you to generate the Microsoft Visual C# code to create any Open XML Format document.

In either case, you can create the document in a MemoryStream object. The following example reads a document into a byte array, creates a resizable memory stream by using the MemoryStream constructor that takes no arguments, writes the byte array to the MemoryStream, alters that document in memory, and then uploads the newly altered document to the Shared Documents library of a SharePoint site.

To run this example, create a word-processing document named Template.docx, initialize it with any content that you want (including no content at all), and put it in the bin directory where you run the example. The example inserts a new paragraph at the beginning of the document before uploading it to the document library.

using System;
using System.IO;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        byte[] byteArray = System.IO.File.ReadAllBytes("Template.docx");
        using (MemoryStream memoryStream = new MemoryStream())
        {
            memoryStream.Write(byteArray, 0, (int)byteArray.Length);
            using (WordprocessingDocument doc =
                WordprocessingDocument.Open(memoryStream, true))
            {
                // Insert a new paragraph at the beginning of the
                //document.
                doc.MainDocumentPart.Document.Body.InsertAt(
                    new Paragraph(
                        new Run(
                            new Text("Newly inserted paragraph."))), 0);
            }
            string fileUrl = "/Shared Documents/NewDocumentFromTemplate.docx";
            memoryStream.Seek(0, SeekOrigin.Begin);
            ClientOM.File.SaveBinaryDirect(clientContext, fileUrl, memoryStream, true);
        }
    }
}

Processing All Documents in a Library

Sometimes you want to iterate through all documents in a document library, performing the same query or modification on each document. The document library may contain folders that contain documents and possibly sub-folders. The following example shows how to use the Scope=’Recursive’ attribute of the View element to process all documents in a document library. This includes documents that are in sub-folders.

Warning

If you are processing all documents in a large document library that contains more than 2000 documents, you must implement a paging algorithm. For details, see Using the SharePoint Foundation 2010 Managed Client Object Model. If you are building an interactive application to do this, you may want to use asynchronous processing. For more information, see Asynchronous Processing.

using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static private void CopyStream(Stream source, Stream destination)
    {
        byte[] buffer = new byte[32768];
        int bytesRead;
        do
        {
            bytesRead = source.Read(buffer, 0, buffer.Length);
            destination.Write(buffer, 0, bytesRead);
        } while (bytesRead != 0);
    }

    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        List sharedDocumentsList = clientContext.Web.Lists
            .GetByTitle("Shared Documents");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml =
            @"<View Scope='Recursive'>
                <Query>
                  <Where>
                    <Eq>
                      <FieldRef Name='File_x0020_Type'/><Value Type='Text'>docx</Value>
                    </Eq>
                  </Where>
                </Query>
              </View>";
        ListItemCollection listItems =
            sharedDocumentsList.GetItems(camlQuery);
        clientContext.Load(listItems);
        clientContext.ExecuteQuery();
        foreach (var item in listItems)
        {
            Console.WriteLine("FileLeafRef: {0}", item["FileLeafRef"]);
            Console.WriteLine("FileDirRef: {0}", item["FileDirRef"]);
            Console.WriteLine("FileRef: {0}", item["FileRef"]);
            FileInformation fileInformation =
                ClientOM.File.OpenBinaryDirect(clientContext,
                (string)item["FileRef"]);
            using (MemoryStream memoryStream = new MemoryStream())
            {
                CopyStream(fileInformation.Stream, memoryStream);
                using (WordprocessingDocument doc =
                    WordprocessingDocument.Open(memoryStream, true))
                {
                    var firstParagraph = doc.MainDocumentPart.Document
                        .Body.Elements<Paragraph>().FirstOrDefault();
                    if (firstParagraph != null)
                    {
                        string text = firstParagraph.InnerText;
                        Console.WriteLine("  First paragraph text: {0}",
                            text);
                    }
                    else
                        Console.WriteLine(
                            "Document doesn't contain any paragraphs.");
                }
            }
            Console.WriteLine();
        }
    }
}

When processing all Open XML Format documents in a document library or folder, it is convenient to use a CAML query to filter for documents of the desired document type, "docx" in this example. The emphasized code in the Collaborative Application Markup Language (CAML) query in the example shows how to do this.

The PowerTools for Open XML project contains a library to accept all tracked revisions for a word-processing document. It is an easy modification to this example to create a program that iterates through all documents in a document library, accepting changes for each. To find the RevisionAccepter class, see the Download section of PowerTools for Open XML.

Creating Open XML Format Word-Processing Documents from Wiki Libraries

Using the Open XML API together with the client object model encourages innovative solutions. For example, a wiki user may want distribute a document to people who do not have access to that wiki site. Those people may be customers or vendors who do not have access to the corporate network, but need the information that is contained in the wiki. You can convert a SharePoint wiki library to an Open XML Format document that you can send to anyone who has Microsoft Office Word 2007 or Microsoft Word 2010. This section shows how to do this by using the client object model.

Use the altChunk capabilities of the Open XML Format to do this. WordprocessingML provides the capability for importing external content through the altChunk element. To use altChunk, do the following:

  • You create an additional part in the package. The part can have several content types. This includes whole Open XML Format documents, HTML, or plain text. In this example, you use the ability to import HTML.

  • You store the content that you want to import into the part.

  • When you create the part, you create a relationship from the main document part to the new alternative format part.

  • You add a w:altChunk element at the location where you want to import the alternative format content. The r:id attribute of the w:altChunk element identifies the content to import. The w:altChunk element is block-level content, which means that you can insert it in the document anywhere you can insert a paragraph (w:p) element.

    This example extracts the HTML for each topic in the wiki, and inserts each page into its own part, and imports each part into the document by using w:altChunk element.

Important

One important point to make about altChunk is that it is used only for importing content. If you open the document by using Word 2007 or Word 2010 and save it, the newly-saved document does not contain the alternative format content part, nor the altChunk markup that refers to it. Word 2007 or Word 2010 converts the HTML to WordprocessingML markup such as paragraph elements (w:p), run elements (w:r), and text elements (w:t).

This next example extracts the contents of each wiki page as HTML.

using System;
using System.Xml.Linq;
using Microsoft.SharePoint.Client;

class RetrieveListItems
{
    static void Main()
    {
        ClientContext clientContext = new ClientContext("http://intranet.contoso.com");
        List oList = clientContext.Web.Lists.GetByTitle("Eric's Wiki");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml = @"<View/>";
        ListItemCollection listItems = oList.GetItems(camlQuery);
        clientContext.Load(
             listItems,
             items => items.Include(
                 item => item["FileRef"],
                 item => item["WikiField"]));
        clientContext.ExecuteQuery();
        foreach (ListItem oListItem in listItems)
        {
            Console.WriteLine("FileRef: {0}", oListItem["FileRef"]);
            XElement e = XElement.Parse((string)oListItem["WikiField"]);
            Console.WriteLine(e);
            Console.WriteLine("====================================");
        }
    }
}

Next, change the URL that is passed to the [M:Microsoft.SharePoint.Client.ClientContext.#Ctor] constructor to the URL of the SharePoint site. Change the title that is passed to the GetByTitle() method to the title of a wiki library in the SharePoint site.

This example uses LINQ to XML to format (indent) the XHTML retrieved from the wiki library. When you run this example, you see something such as the following.

FileRef: /Erics Wiki/AnotherPage.aspx
<div class="ExternalClass7EB3EF2C25AB481081BEE2BF8D80B6B8">
  <table id="layoutsTable" style="width:100%">
    <tbody>
      <tr style="vertical-align:top">
        <td style="width:100%">
          <div class="ms-rte-layoutszone-outer" style="width:100%"><div><p>This is some content in another page.</p><p> </p><p><strong>Here is some bold text.</strong></p><p></p></div></div>
        </td>
      </tr>
    </tbody>
  </table>

  <span id="layoutsData" style="display:none">false,false,1</span>
</div>
====================================
FileRef: /Erics Wiki/This is a longer title of a page.aspx
<div class="ExternalClass980D47DB1C51449E9684343F131C5689">
  <table id="layoutsTable" style="width:100%">
    <tbody>
      <tr style="vertical-align:top">
        <td style="width:100%">
          <div class="ms-rte-layoutszone-outer" style="width:100%"><div><p>This page contains some content we will extend in the future.</p><p></p></div></div>
        </td>
      </tr>
    </tbody>
  </table>

  <span id="layoutsData" style="display:none">false,false,1</span>
</div>

As you can see, the contents of each page in the wiki library are contained in a <div> element. Inside the <div> element is a table element that the wiki uses for layout. The element that you are really interested in is the <div> element inside the table cell that has a class attribute that has the value of ms-rte-layoutszone-outer. When we construct the HTML to insert in the alternative format import part, you insert that inner <div> element into the <body> element of the HTML document. The code example then writes the newly constructed HTML into the alternative content part.

Visual Basic noteVisual Basic Note

This conversion does not convert wiki links to bookmarks and links within the document. In addition, there are certain characters that can be inserted in the wiki page that do not convert correctly. It is a proof-of-concept, and is intended to show the possibilities of using the client object model with the Open XML Format API. It is not intended to solve all issues associated with conversion of a SharePoint wiki to an Open XML Formats document.

Here is the example to extract the contents of all pages of a SharePoint wiki, and create a word-processing document from them.

using System;
using System.Linq;
using System.IO;
using System.Xml.Linq;
using Microsoft.SharePoint.Client;
using ClientOM = Microsoft.SharePoint.Client;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

class RetrieveListItems
{
    static void Main()
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        List oList = clientContext.Web.Lists.GetByTitle("Eric's Wiki");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml = @"<View/>";
        ListItemCollection listItems = oList.GetItems(camlQuery);
        clientContext.Load(
             listItems,
             items => items.Include(
                 item => item["FileLeafRef"],
                 item => item["WikiField"]));
        clientContext.ExecuteQuery();

        System.IO.File.Delete("WikiDocument.docx");
        System.IO.File.Copy("EmptyDocument.docx", "WikiDocument.docx");
        int altChunkIdCounter = 1;
        int blockLevelCounter = 0;
        bool first = true;
        using (WordprocessingDocument myDoc =
            WordprocessingDocument.Open("WikiDocument.docx", true))
        {
            MainDocumentPart mainPart = myDoc.MainDocumentPart;
            mainPart.Document.Body.RemoveAllChildren<Paragraph>();
            foreach (ClientOM.ListItem listItem in listItems)
            {
                string wikiPageTitleAspx = (string)listItem["FileLeafRef"];
                string wikiPageTitle = wikiPageTitleAspx.Substring(
                    0, wikiPageTitleAspx.Length - 5);
                if (first)
                {
                    first = false;
                    mainPart.Document.Body.InsertAt(new Paragraph(
                        new ParagraphProperties(
                            new RunProperties(
                                new RunFonts() {
                                    AsciiTheme = ThemeFontValues.MajorHighAnsi,
                                    HighAnsiTheme = ThemeFontValues.MajorHighAnsi },
                                new Bold(),
                                new Color() { Val = "1F497D",
                                    ThemeColor = ThemeColorValues.Text2 },
                                new FontSize() { Val = 28 },
                                new FontSizeComplexScript() { Val = 28 }),
                            new ParagraphStyleId() { Val = "Heading1" }),
                        new Run(
                            new RunProperties(
                                new RunFonts() {
                                    AsciiTheme = ThemeFontValues.MajorHighAnsi,
                                    HighAnsiTheme = ThemeFontValues.MajorHighAnsi },
                                new Bold(),
                                new Color() { Val = "1F497D",
                                    ThemeColor = ThemeColorValues.Text2 },
                                new FontSize() { Val = 28 },
                                new FontSizeComplexScript() { Val = 28 }),
                            new Text(wikiPageTitle))),
                        blockLevelCounter++);
                }
                else
                {
                    mainPart.Document.Body.InsertAt(new Paragraph(
                        new Run(
                            new Break() { Type = BreakValues.Page } )),
                        blockLevelCounter++);
                    mainPart.Document.Body.InsertAt(new Paragraph(
                        new ParagraphProperties(
                            new RunProperties(
                                new RunFonts() {
                                    AsciiTheme = ThemeFontValues.MajorHighAnsi,
                                    HighAnsiTheme = ThemeFontValues.MajorHighAnsi },
                                new Bold(),
                                new Color() { Val = "1F497D",
                                    ThemeColor = ThemeColorValues.Text2 },
                                new FontSize() { Val = 28 },
                                new FontSizeComplexScript() { Val = 28 }),
                            new ParagraphStyleId() { Val = "Heading1" }),
                        new Run(
                            new RunProperties(
                                new RunFonts() {
                                    AsciiTheme = ThemeFontValues.MajorHighAnsi,
                                    HighAnsiTheme = ThemeFontValues.MajorHighAnsi },
                                new Bold(),
                                new Color() { Val = "1F497D",
                                    ThemeColor = ThemeColorValues.Text2 },
                                new FontSize() { Val = 28 },
                                new FontSizeComplexScript() { Val = 28 }),
                            new Text(wikiPageTitle))),
                        blockLevelCounter++);
                }
                XElement wikiField = XElement.Parse((string)listItem["WikiField"]);
                XElement div = wikiField.Descendants("div").Where(e =>
                    (string)e.Attribute("class") == "ms-rte-layoutszone-inner" )
                    .FirstOrDefault();
                string html = String.Format(
                    "<html><head/><body>{0}</body></html>",
                    div != null ? div.ToString() : "");
                string altChunkId = String.Format("AltChunkId{0}",
                    altChunkIdCounter++);
                AlternativeFormatImportPart chunk =
                    mainPart.AddAlternativeFormatImportPart(
                    AlternativeFormatImportPartType.Xhtml, altChunkId);
                using (Stream chunkStream = chunk.GetStream(FileMode.Create,
                    FileAccess.Write))
                using (StreamWriter stringStream = new StreamWriter(chunkStream))
                    stringStream.Write(html);
                AltChunk altChunk = new AltChunk();
                altChunk.Id = altChunkId;
                mainPart.Document.Body.InsertAt(altChunk,
                    blockLevelCounter++);
            }
            mainPart.Document.Save();
        }
    }
}

 

It provides lots of functionality in fewer than 130 lines of code. Here is how one of the pages in the SharePoint wiki appears.

Figure 1. Page on a wiki library

Figure 1

 

The same page, as shown in Word 2010 looks as follows:

Figure 2. Same page as a Open XML Format word-processing document

Figure 2

 

Conclusion

The SharePoint Foundation 2010 managed client object model enables you to write powerful client-side applications that work with SharePoint Foundation 2010 and SharePoint Server 2010. The Open XML SDK 2.0 enables you to create, modify, and query Open XML Format documents. When used together, they enable new ways of using Open XML documents to collaborate and share information.

Additional Resources

This article combines two technologies: the Open XML SDK 2.0, and the SharePoint Foundation 2010 managed client object model. The place to start with Open XML is the Open XML Developer Center on MSDN. There is lots of content there. This includes articles, how-to videos, and links to numerous blog posts. In particular, the following links provide important information for get started with the Open XML SDK 2.0:

There are many resources to help you start working with the SharePoint Foundation 2010 managed client object model. First, read Using the SharePoint Foundation 2010 Managed Client Object Model. In addition, the following are good resources for the client object model: