Welcome to MSDN Blogs Sign in | Join | Help

Again one blog entry from the future;) I hope you enjoyed my session about Composite UI Application Block and Smart Client Software Factories at TechEd Europe in Barcelona, as well.

I know this session was fully packaged and the time was very short for digging into all of the details of the framework componets I created. But here you find these framework components together with the finished sample solution for download.

My primary goal with this session really was sharing ideas with you of how you could solve certain, very special problems with CAB/SCSF May 2007 release and I hope that you have been inspired by the session to think about your own patterns or re-use/extend the patterns I've introduced.

Here you find the sample code and "Alpha-versions" of the frameworks for download, that I've implemented for the TechEd-session.

I know this is just a starting-point but I think I did many thoughs for you, already, you would have been required to do on your own when thinking about things such as Context-sensitive behavior or workflow-integration. If you want to dig in my framework components, here are the projects with the appropriate components packaged into them:

  • Conext sensitive behavior (IContextManagerService & implementation)
    • mszCool.Samples.Practices.CompositeUI.Context
  • Workflow Interfaces Library (IWorkflowService, WorkflowService, External Data Exchange Interfaces and Services)
    • mszCool.Samples.Practices.CompositeUI.Workflow.Interfaces
  • Workflow Activity Library (RunWorkItem Activity, RunView Activity, HandleRunWorkItemCompleted, TransferState, CallService)
    • mszCool.Samples.Practices.CompositeUI.Workflow.Activities
  • Framework for abstracting away type information of WorkItems into "user task names" so that it is easier for users to identify workitems they want to run. Basically the module then needs to register workitems as tasks in a central task-service that is then used by the WorkflowWorkItem to find the workitem type it needs to run through a dictionary. In the final implementation this should be read from a database for example...
    • mszCool.Samples.Practices.CompositeUI.Workflow.Tasks

Have much fun with it... I'd be happy to see this at some time in the future in a codeplex workspace (unfortunately I can only share this samples but not the customer-implementation that will be developed;))...

I thought I'll publish the demos of my TechEd sessions... so I am writing kind of in a time-travel-style here;).

I hope you enjoyed the littel fun-demo-session on Wednesday at TechEd in Barcelona on Office Development. Here you can find the complete VS2008 solution and demo parts that I used for building the litte community solution with SharePoint, VSTO 2008 and Office Open XML...

Download the files here...

I'd be happy about any feedback personal feedback on this session as the numbers and scores are often not enough to really improve things;) Looking forward to meet you next year again...

As I just answered this question for a customer: of course it is possible to digitally sign Office Open XML file format packages. This is a core part of the Open Packaging Convention which is based on open standards such as W3C XML Digital Signatures and X.509 certificates. You can even apply more signatures on one document - programmatically or directly from within Office (Word, Excel or PowerPoint). For more information take a look at the following links:

These are just a couple of interesting links. Of course if you are interested in all the details of the standard, the Open Packaging Convention part of the standard is the place to look at:

Hope that helps others as well;)

This blog entry is all about dynamically adding content controls on the server to a word document and binding them to content of a custom XML data island which is generated on the server as well! You can download the sample code here.

With my last blog entry I started a little series about generating Word documents on the server using the ECMA Office Open XML File Formats and the System.IO.Packaging API. I mentioned a couple of options for generating documents through code and the one I presented in the first post used the simplest approach available - embedding a custom XML document using the Word Content Control toolkit into the Word document and using content controls to bind parts of the document to elements of this custom XML.

The biggest advantage of the solution I presented was its simplicity: generating the document on the server was not more than creating object instances of classes based on the XML schema of the embedded custom XML and serializing it into a package part of the Open XML package. As the content controls where databound to the parts of the custom XML the contents of the document were updated automatically when the user opened the document with Word. On the other hand the biggest drawback finally was, that we had a fixed structure of our document which was not extensible. We embedded a custom XML with a limited number of invoice items and bound them to content controls - in our example we used 5 invoice items. For many cases this might be enough, but for other cases this can be an inaceptable limitation.

The obvious extension to our project

Of course we build on top of the project we started creating in the last post. The obvious extension to the approach of course is modifying our code so that it dynamically extends the Word table used for displaying the invoice items for each new invoice item which is dynamically added to the custom XML of the document. That means our template is not bound to exactly 5 invoice items which are hard-bound to the appropriate 5 invoice items in our custom XML as it was the case in our last version. Basically that means we can work with any number of invoice items serialized into our custom XML and that we need to dynamically add new rows to the Word table of our template and dynamically add content controls (structured document tags) to our Word table rows which are bound to the appropriate InvoiceItem element in our custom XML.

clip_image003[3][1][1][1]

What are typical scenarios for this approach

Again you have a fixed structure of your document which needs to be extended based on content you get from some sources such as databases, services etc. on the server side or from within your application. The structure of the parts which are extended does not change, but it needs to be extended with something like repeating sections. Most important: you need to keep the document surface (UI) in sync with the underlying data source and the underlying data source needs to be updated whenever the user changes the document as this datasource will be processed on the server or by your application programmatically afterwards.

How does this approach work?

Basically this approach is an extension of the approach introduced in the last blog posting of this series. Again you serialize a custom XML data island into your package using XmlSerializer but this time you do not limit the number of invoice items in your custom XML. Therefore it adds a second step: after you have generated the custom XML on the server and serialized it into the package, you need to process the Word Processing ML on the server as well as you need to clone table row nodes in your Word table and dynamically add content controls (structured document tags) where you then modify the dataBinding attribute to point to the invoice item in the custom XML for which the table row has been created.

What are the advantages of this approach?

  • You maintain a clear separation of the document UI (document content in Word ML) and your actual business data through custom XML as with the approach demonstrated in the previous posting of this article series.
  • Therefore you still can write program logic that works directly with your business-data structure based on an XML schema.
  • With content control, business data is automatically updated in both, the document and the custom XML data island without writing additional, client sode code.

What are the disadvantages of this approach?

  • It works with Word 2007, only. Although you can open the files with older versions of Office without any problems, older versions of Office don't have the functionality of Content Controls available as this was not implemented in previous Office versions, yet.
  • This approach is harder to implement than the one introduced in the previous posting and it requires you having much deeper know-how about the Word Processing Markup language (Word ML).
  • You need to work with some sort of XML API - the one I used is XML DOM with the XmlDocument object which might lead to performance problems on heavy load. This disadvantage can be mitigated by using XmlReader / XmlWriter technologies. But a switch to XmlReader / XmlWriter does not come without a drawback: the logic gets a little harder to implement compared to XmlDocument.

    Extending our Sample Implementation

    The first step for extending the sample implementation we started creating in the last posting is adding a new button to the ASP.NET application for dynamically adding new Invoice Items to our Invoice - as shown here:

    protected void Button4_Click(object sender, EventArgs e)
    {
        // Add a new invoice node
        InvoiceElement Invoice =
            Session[InvoiceAdapter.CurrentInvoiceTemplateKey] as InvoiceElement;
    
        InvoiceElementInvoiceItem[] NewArray = 
            new InvoiceElementInvoiceItem[Invoice.InvoiceItems.Length + 1];
        Invoice.InvoiceItems.CopyTo(NewArray, 0);
    
        NewArray[NewArray.Length - 1] = new InvoiceElementInvoiceItem();
        NewArray[NewArray.Length - 1].ItemNumber = NewArray.Length;
        NewArray[NewArray.Length - 1].ItemName = "[new item]";
    
        Invoice.InvoiceItems = NewArray;
    
        InvoiceItemSource.Select();
        GridView1.DataBind();
    }

    This event procedure gets the current Invoice from the ASP.NET session and adds a new InvoiceItem. As XSD.EXE did not generate nice, handy List<T> properties we need to extend the array for our invoice items manually. Nice, isn't it:) Anyway - for each invoice item which we add this way to our object graph we need to generate a separate table row in the word table with - and that's important now - new content controls bound to the appropriate row. Okay, that means our event handling routine of the button used for generating the document now needs to update the custom XML with the invoice we've created in our ASP.NET page as well as update the Word document itself to add new content controls as follows.

    protected void Button2_Click(object sender, EventArgs e)
    {
        // First of all we retrieve the invoice
        InvoiceAdapter.CalculateTotals();
        InvoiceElement Invoice =
            Session[InvoiceAdapter.CurrentInvoiceTemplateKey] as InvoiceElement;
    
        // First of all copy the template for this approach
        string templatePath = Server.MapPath("~/InvoiceTemplate_Hard.docx");
        string generatedPath = Server.MapPath
                               (
                                    string.Format("~/Generated/Invoice{0}.docx",
                                                  Invoice.InvoiceNumber)
                               );
        if (File.Exists(generatedPath))
            File.Delete(generatedPath);
        File.Copy(templatePath, generatedPath);
    
        // Now open the package using the packaging API and generating content
        using (Package p = Package.Open(generatedPath))
        {
            UpdateCustomXml(p, Invoice);
            UpdateWordDocument(p, Invoice);
            p.Flush();
        }
    
        StatusLabel.Text = 
            string.Format("Successfully generated invoice Invoice{0}.docx!!", 
                          Invoice.InvoiceNumber);
    }

    Again we copy a template (this time called InvoiceTemplate_Hard.docx) and copy it to our folder for generated invoices. Before that we calculate the totals of our invoice using the business object adapter I've included and then we retrieve the Invoice from the current ASP.NET session. Afterwards we simply open the package, but this time just serializing the invoice object as custom XML part as we did it last time is not enough. This is just one of two steps which I've encapsulated into a method called UpdateCustomXml(package, InvoiceElement) as follows:

    private void UpdateCustomXml(Package p, InvoiceElement Invoice)
    {
        // Here we open up the custom XML part included in the word document
        // and append new invoice items to our invoice
        Uri XmlPartUri = new Uri("/customXml/item1.xml", UriKind.Relative);
        PackagePart XmlPart = p.GetPart(XmlPartUri);
    
        // Now we just serialize the invoice into the custom XML
        // This time we can create as many invoice items as we want.
        using (Stream s = XmlPart.GetStream(FileMode.Create))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(InvoiceElement));
            serializer.Serialize(s, Invoice);
        }
    }

    Still this very simple, but now we are getting to the much harder part - updating the Word document for extending the Word table with new content controls bound to the appropriate parts of our custom XML serialized into the package in the UpdateCustomXml method. The steps for doing this are the following:

    1. First we load the Word document into an XmlDocument instance.
    2. Then we create an XmlNamespaceManager with the Word namespace as we will execute some XPath queries while processing the document.
    3. Next we will retrieve the <w:tbl /> node through an XPath expression.
    4. Then we will retrieve the second row (!!) of the table as this is the first real content row (the first row contains the headers and the last row contains the summary.
    5. For each invoice item in our invoice we clone the previously selected row and update the databindings of the content controls, update their aliases and IDs as they need to be unique.
    6. Last but not least we save the modified XmlDocument back to the package part to update the Word document (Word ML part of the document).

    This logic is encapsulated into the UpdateWordDocument(Package, InvoiceElement) method as you can see below:

    private void UpdateWordDocument(Package p, InvoiceElement Invoice)
    {
        // Step 1: loading the Word document into an XmlDocument
    Uri WordDocUri = new Uri("/word/document.xml", UriKind.Relative); PackagePart WordDocPart = p.GetPart(WordDocUri); XmlDocument WordDoc = new XmlDocument(); using (Stream s = WordDocPart.GetStream()) { WordDoc.Load(s); } // Step 2: Create an XmlNamespaceManager for XPath queries XmlNamespaceManager NsMgr = new XmlNamespaceManager(WordDoc.NameTable); NsMgr.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"); // Step 3 and step 4: Selecting the table node and the first row
    XmlNode TableNode = WordDoc.SelectSingleNode("//w:tbl", NsMgr); XmlNode FirstRowNode = TableNode.SelectSingleNode(".//w:tr[2]", NsMgr); XmlNode NewNode = null, PreviousNode = null;
        // Step 5: Creating a new row in the row for each item in the invoice
    // and update the content control databindings to bind to the
    // appropriate element in the custom XML part
    for (int i = 0; i < Invoice.InvoiceItems.Length; i++) { // Current item InvoiceElementInvoiceItem Item = Invoice.InvoiceItems[i]; // The first node can be modified directly if (NewNode == null) { PreviousNode = NewNode = FirstRowNode; } else { // Keep the previous node to be able to // insert the node at the right position NewNode = FirstRowNode.CloneNode(true); TableNode.InsertAfter(NewNode, PreviousNode); PreviousNode = NewNode; } // Update the SDT tags in the document XmlNodeList SdtNodes = NewNode.SelectNodes(".//w:sdt", NsMgr); UpdateSdtNode(SdtNodes[0], i + 1, 1, string.Format("ItemNr_{0}", i), NsMgr); UpdateSdtNode(SdtNodes[1], i + 1, 2, string.Format("ItemDescription_{0}", i), NsMgr); UpdateSdtNode(SdtNodes[2], i + 1, 3, string.Format("ItemAmount_{0}", i), NsMgr); UpdateSdtNode(SdtNodes[3], i + 1, 4, string.Format("ItemPrice_{0}", i), NsMgr); UpdateSdtNode(SdtNodes[4], i + 1, 5, string.Format("ItemTotals_{0}", i), NsMgr); } // Step 6: Save the XmlDocument back to the document.xml part
    using (Stream s = WordDocPart.GetStream(FileMode.Create)) { WordDoc.Save(s); } }

    Most of the logic in this method is plain XML processing and should be nothing new for a .NET developer. The most interesting part is encapsulated in the UpdateSdtNode() method highlighted in bold in the previous code snippet. SDT is the abbreviation for Structured Document Tags and SDTs are actually the reflection of content controls in the XML part of the document. Taking a look at the following XML excerpt of a typicall Word document with a content control bound to parts of a custom XML will explain the most important parts of an SDT:

    <w:tr w:rsidR="00204484" w:rsidTr="00204484">
      <w:trPr>
        <w:cnfStyle w:val="000000100000" />
      </w:trPr>
      <w:sdt>
        <w:sdtPr>
          <w:dataBinding 
    w:prefixMappings="xmlns:ns0='blogs.msdn.com/mszcool/officeopenxml/generatingdocumentsseries/2007/07'"
    w:xpath="/ns0:InvoiceElement[1]/ns0:InvoiceItems[1]/ns0:InvoiceItem[1]/ns0:ItemNumber[1]"
    w:storeItemID="{C941DEB9-29BE-4021-950D-34892903E6EE}" /> <w:rPr> <w:lang w:val="en-US" /> </w:rPr> <w:alias w:val="ItemNr_01" /> <w:tag w:val="ItemNr_01" /> <w:id w:val="17763868" /> <w:placeholder> <w:docPart w:val="E5C03C5D0D364E298E9CFE332768F056" /> </w:placeholder> <w:showingPlcHdr /> <w:text /> </w:sdtPr> <w:sdtContent> <w:tc> <w:tcPr> <w:cnfStyle w:val="001000000000" /> <w:tcW w:w="675" w:type="dxa" /> </w:tcPr> <w:p w:rsidR="00204484" w:rsidRDefault="00864DE0" w:rsidP="00204484"> <w:pPr> <w:rPr> <w:lang w:val="en-US" /> </w:rPr> </w:pPr> <w:r> <w:t>0</w:t> </w:r> </w:p> </w:tc> </w:sdtContent> </w:sdt>
    ........ other tags .........
    </w:tr>

    The XML is just an excerpt of one table row showing the SDT tag for the content control which is bound to the ItemNumber element of the first InvoiceItem stored in the custom XML of our package. You can see this when taking a look at the dataBinding element of the SDT tag where you find an XPath expression mapping to an element in our custom XML:

    w:xpath="/ns0:InvoiceElement[1]/ns0:InvoiceItems[1]/ns0:InvoiceItem[1]/ns0:ItemNumber[1]"

    As you can see, this XPath maps to the first ItemNumber element in the first InvoiceItem element of the first InvoiceItems element within the first InvoiceElement of our custom XML (based on the indexes [1] used in the XPath). If we want to update the databinding of a content control we need to update this XPath expression to map to the invoice item in our custom XML we want to display in a certain table row of our Word table - e.g. if we want to map to the third invoice item the XPath would look as follows:

    w:xpath="/ns0:InvoiceElement[1]/ns0:InvoiceItems[1]/ns0:InvoiceItem[3]/ns0:ItemNumber[1]"

    Everything in our XPath stays the same, we just update the index of the invoice item to map to the third InvoiceItem element of the custom XML part we serialized into our package with the UpdateCustomXml() method before. Furthermore we need to update attribute w:val for the w:alias and the w:id tags as they need to be unique for each content control. The value of the w:alias can be any value you want whereas the w:id value needs to be a integer. These updates are performed all together in the UpdateSdtNode() method used in our previous code snippet (UpdateWordDocument() method). It sounds complicated but it is fairly simple:

    private void UpdateSdtNode(XmlNode xmlNode, int index, int elementIndex, string alias, XmlNamespaceManager nsMgr)
    {
        XmlNode HelperNode = null;
    
        // Update the databinding node
        HelperNode = xmlNode.SelectSingleNode(".//w:sdtPr/w:dataBinding", nsMgr);
        HelperNode.Attributes["w:xpath"].Value =
            HelperNode.Attributes["w:xpath"].Value.Replace
            (
                "ns0:InvoiceItem[1]",
                string.Format("ns0:InvoiceItem[{0}]", index)
            );
    
        // Update the alias and tag nodes
        HelperNode = xmlNode.SelectSingleNode(".//w:sdtPr/w:alias", nsMgr);
        HelperNode.Attributes["w:val"].Value = alias;
        HelperNode = xmlNode.SelectSingleNode(".//w:sdtPr/w:tag", nsMgr);
        HelperNode.Attributes["w:val"].Value = alias;
    
        // Update the ID node
        HelperNode = xmlNode.SelectSingleNode(".//w:sdtPr/w:id", nsMgr);
        HelperNode.Attributes["w:val"].Value =
            (1000000 + (elementIndex * 1000) + (index * 10)).ToString();
    
    }

    That's it, now we have generated a Word document by dynamically extending the Word table <w:tbl/> and generating new Structured Document Tags (<w:sdt />) for each row which are in turn bound to content of our custom XML structure.

    You can download the most recent version of application including the code introduced in this blog post by clicking on the link below:

    Download sample code

    In my last workshops together with Andreas about ECMA Office Open XML file formats I demonstrated a way for generating Word documents using the packaging API and custom XML data islands. But actually the possibility I presented in the workshop is not the only one... and I have to admit... it's not the easiest one.

    Therefore I thought I'll share my thoughts on generating documents without the need of having Office installed on your machine based on the new file format including the advantages and disadvantages for each approach. The options I'll take a look at are the following ones (of course you can find downloads for samples I've created always at the end of each blog-entry):

    Easiest way possible Using Custom XML islands together with content controls
    Hard and powerful Using Custom XML islands together with dynamically generated content controls
    Medium but still powerful Custom attached schema to mark content in document relevant for automated generation
    Out of discussion (in my oppinion) Invent custom "markup tags" for marking content in documents and generated based on these

    In this and subsequent blog entries I'll discuss each of these ones including their advantages and disadvantages. For this blog I'll start off with the easiest one - just leveraging custom XML data islands.

    Generating Documents with Custom XML Data Islands & Content Controls

    This approach leverages the new functionality introduced with Word 2007 with custom XML data islands and content controls. Your code which is generating the document (probably running on the server) just updates the custom XML island in the Office Open XML package and the content is bound to the document surface using content controls.

    What are typical characteristics of scenarios for this approach?
    You have a fixed form-structure for your document that does not need to be extended, dynamically. You just need to fill in information into fields and fields of "fixed-sized" tables on the server-side without extending Word-specific markup (such as adding new rows to tables).

    How does this approach work?
    The first thing you need to do is agreeing on the data-structure you are working with. This data-structure models a schema that describes the business-information stored in your document. Based on this schema you can create a document template, design it's UI with content controls and bind the controls using the Word Content Control Toolkit published on Codeplexx. On the server you simply use the System.IO.Packaging API shipping with the .NET Framework 3.0 for modifying the custom XML. Whenever the user opens the document in Word 2007, information from the custom XML is bound to the document UI automatically and any changes in the document are reflected back to the custom XML data island. More details later in this blog...

    What are the advantages of this approach?

    • It's very easy to implement.
    • You can write program logic that works directly with your business-data structure based on an XML schema.
    • With content control, business data is automatically updated in both, the document and the custom XML data island without writing additional, client sode code.
    • You don't know anything about Word programming on the client or Word Processing ML in your custom code.

    What are the disadvantages of this approach?

    • It works with Word 2007, only. Although you can open the files with older versions of Office without any problems, older versions of Office don't have the functionality of Content Controls available as this was not implemented in previous Office versions, yet.
    • This is a very limited approach because it does not allow you to extend content in the document such as table rows, columns or complete sections in the document at all.

    Sample Implementation

    Last but not least I want to round off this first blog entry with a simple sample implementation demonstrating this approach. The first thing is starting up with an XML schema describing our content. Let's assume we are using Word for automatically generating invoice documents. A schema for an invoice could look similar to the following (we assume this schema for the remaining post and the subsequent posts I'll create on this topic):

    <xs:schema targetNamespace="blogs.msdn.com/mszcool/officeopenxml/generatingdocumentsseries/2007/07" elementFormDefault="qualified" xmlns="blogs.msdn.com/mszcool/officeopenxml/generatingdocumentsseries/2007/07" xmlns:mstns="blogs.msdn.com/mszcool/officeopenxml/generatingdocumentsseries/2007/07" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:element name="InvoiceElement">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="InvoiceNumber" type="xs:string" />
            <xs:element name="InvoiceDate" type="xs:date" />
            <xs:element name="Customer">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="CompanyName" type="xs:string" />
                  <xs:element name="Contact">
                    <xs:complexType>
                      <xs:sequence>
                        <xs:element name="Firstname" type="xs:string" />
                        <xs:element name="Lastname" type="xs:string" />
                        <xs:element name="Email" type="xs:string" />
                      </xs:sequence>
                    </xs:complexType>
                  </xs:element>
                  <xs:element name="Street" type="xs:string" />
                  <xs:element name="ZipCode" type="xs:string" />
                  <xs:element name="City" type="xs:string" />
                  <xs:element name="Country" type="xs:string" />
                </xs:sequence>
              </xs:complexType>
            </xs:element>
            <xs:element name="InvoiceItems">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="InvoiceItem" maxOccurs="unbounded">
                    <xs:complexType>
                      <xs:sequence>
                        <xs:element name="ItemNumber" type="xs:int" />
                        <xs:element name="ItemName" type="xs:string" />
                        <xs:element name="ItemAmount" type="xs:decimal" />
                        <xs:element name="ItemUnitPrice" type="xs:decimal" />
                        <xs:element name="ItemToals" type="xs:decimal" />
                      </xs:sequence>
                    </xs:complexType>
                  </xs:element>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
    <xs:element name="InvoiceTotals" type="xs:decimal" /> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>

    Based on this schema we now can generate a class with xsd.exe, a tool shipping with the .NET Framework for generating .NET types out of XML schemas and vice versa. This enables us later to use the XmlSerializer of the .NET Framework System.Xml.Serialization namespace to serialize and de-serialize XML instances based on this schema into the .NET type we generated through xsd.exe. Thus we save ourselves from writing the typical XPath code when working with XML in a DOM;)

    Visual Studio 2005 Command Prompt 

    As I am demoing the stuff in a web application, I copy the generated code file to the App_Code sub directory of my web application. Now we are set to proceed with the next steps. If you don't know why I am doing this right now you will definitely understand better when we start writing the code for generating the document in our web application on the server.

    Next we can design the document for our invoice including the content controls. For finding content controls you need to activate the developer tab ribbon in the Word options. The invoice we're designing could look similar to the following one whereas the content controls I am using are text block (either simple or RTF), drop-down lists and date-time drop-downs.

    image

    As you can see we've marked the parts of the document we would like to bind to our XML schema structure with Word Content Controls from the developer tab ribbon. In the developer tab ribbon you will find these controls in the "Controls" group as you can see in the image above, as well. Furthermore for this simple type of document generation our document contains a fixed number of item rows (in my example 5, but of course it can be more). Each row of the table contains several content controls which we will bind to a row in an XML instance using the word content control toolkit as you can see in the next figure below. But before we can do that we have to create a Sample-XML document that we will add to our document as a Custom XML data island with the Word Content Control Toolkit. The XML document instance based on our schema needs to have 5 InvoiceItem as our template supports 5 items as you can see above (that's it for this rather simple approach, of course you can do it dynamically as well which is more complex and covered in one of my subsequent blog entries).

    <InvoiceElement xmlns="blogs.msdn.com/mszcool/officeopenxml/generatingdocumentsseries/2007/07">
      <InvoiceNumber>12345</InvoiceNumber>
      <InvoiceDate>2007-07-06</InvoiceDate>
      <Customer>
        <CompanyName>Microsoft Austria</CompanyName>
        <Contact>
          <Firstname>Mario</Firstname>
          <Lastname>Szpuszta</Lastname>
          <Email>dpeat@microsoft.com</Email>
        </Contact>
        <Street>Am Euro Platz 3</Street>
        <ZipCode>1120</ZipCode>
        <City>Vienna</City>
        <Country>Austria</Country>
      </Customer>
      <InvoiceItems>
        <InvoiceItem>
          <ItemNumber>0</ItemNumber>
          <ItemName></ItemName>
          <ItemAmount>0</ItemAmount>
          <ItemUnitPrice>0</ItemUnitPrice>
          <ItemToals>0</ItemToals>
        </InvoiceItem>
        <InvoiceItem>
          <ItemNumber>0</ItemNumber>
          <ItemName></ItemName>
          <ItemAmount>0</ItemAmount>
          <ItemUnitPrice>0</ItemUnitPrice>
          <ItemToals>0</ItemToals>
        </InvoiceItem>
        <!-- ... 3 more InvoiceItem Items ... -->
      </InvoiceItems>
      <InvoiceTotals>0</InvoiceTotals>
    </InvoiceElement>
    

    Now let's move on save the previously created Word document into your web project's directory folder as a template, close word, open the Word Content Control toolkit. Create a new custom XML using the Content control toolkit and copy the XML shown above in the edit-mode of the XML pane of the tool. Then switch to bind mode and bind each of the XML elements to one content control in your document as shown below:

    image

    As you can see in the image above, each InvoiceItem's members are bound to different content place holder controls for the document. This document now acts as a template for our solution. The solution we're creating is a simple ASP.NET page that leverages the previously created classes (using XSD.EXE to create classes from the schema) to bind content from the classes to the UI of the application. The application looks similar to the following one and uses ASP.NET object binding to bind the objects' content to the actual UI of the ASP.NET web application. I don't want to spend too much time on ASP.NET in this posting as you can download the sample from the attachments of this post, anyway.

    image

    For this first blog post we implement the event procedure of the "Content Control simple..." button to generate a document based on the first approach which is explained in this simple example. As we use object binding with ASP.NET and have a little, self-written class in place (you can see it when downloading the sample, it's called InvoiceAdapter and adds a pre-populated InvoiceElement instance with 5 InoviceItem instances to the Session which are bound to the UI shown above).

    So assuming that we have an InvoiceElement stored in the session, we can let the user enter information into the page and hit one of the buttons to generate a document based on the information entered into the ASP.NET based UI with a very small amount of code as follows:

    InvoiceElement element = 
        Session[InvoiceAdapter.CurrentInvoiceTemplateKey] as InvoiceElement;
    if (element.InvoiceItems.Length != 5)
    {
        StatusLabel.Text = "You need exactly 5 rows as in your template for this approach!";
        return;
    }
    
    // Then copy the document template
    string templatePath = Server.MapPath("~/InvoiceTemplate.docx");
    string generatedPath = Server.MapPath
                           (
                                string.Format("~/Generated/Invoice{0}.docx", 
                                              element.InvoiceNumber)
                           );
    if (File.Exists(generatedPath))
        File.Delete(generatedPath);
    File.Copy(templatePath, generatedPath);
    
    // Now you can open the copied file using the packaging APIs
    using (Package p = Package.Open(generatedPath, FileMode.Open, FileAccess.ReadWrite))
    {
        // Now we can get the path to our custom XML in the template
        Uri partUri = new Uri("/customXml/item1.xml", UriKind.Relative);
        PackagePart part = p.GetPart(partUri);
        // Overwrite existing part, therefore open with FileMode.Create
    using (Stream s = part.GetStream(FileMode.Create)) { // Now use the XmlSerialize to write back the content XmlSerializer serializer = new XmlSerializer(typeof(InvoiceElement)); serializer.Serialize(s, element); } // Flush memory-content back to the package p.Flush(); } StatusLabel.Text = "Document generated as " + string.Format("Invoice{0}.docx", element.InvoiceNumber);

    That's fairly easy - and we've reached an attractive goal with that little amount of code. Think of what that meant in the past when you had to work with the good, old COM automation object model (which meant you had to install Office on the server). Now you don't need to install Office on the server to generate documents and generating documents is just simple XML processing.

    Wrap-Up and final comments

    That's of course the easiest way for generating documents on the server. But actually it's so simple and restricted that I'd rather call it "filling in business information on the server" instead of "generating documents":)) The biggest advantage of this approach is, that it is (a) simple and (b) if you need to process the information entered or modified by the user on the server it works the same, simple way technically. Still for professional document generation we need more possibilities, of course. Therefore I'll take a look at other approaches in subsequent posts;)

    You can download the sample I've created for showing this approach here.

    Last Monday I did a 2 hour nearly demo-only session at the technical university in Vienna to show some of the nitty-gritty details of ASP.NET AJAX. It was a more detailled version (especially in demos on the AJAX stuff) of the portal I created at our last Big Days road show. As the task was building a web site from scratch I started building this site with Expression Web designer and then completed it using Visual Studio ORCAS. Although - because still in early beta - ORCAS sometimes has some stability issues the imageroundtripping between these two worlds, Expression Web and ORCAS worked seamless although ORCAS ships with a new version of the AJAX Extensions integrated into the .NET Framework 3.5.

    Still the AJAX Extensions provide the same functionality as they do with the version available at http://ajax.asp.net - therefore I ported the web site back to work seamless with released-only technologies which aren't in beta. Attached to this posting you can find the samples and PowerPoint slides I presented at the university. The samples attached to this post can be opened with Visual Studio 2005 without any problems. Actually back-porting took me about 10 mins. incl. testing and setting up the database as a file-database using the freely available SQL Express so that you do not need to attach it to your SQL Server.

    Required software to run the sample therefore is:

    Basically that's it (I know, developers from Microsoft field know these link above, but the presentation was given to primarily non-Microsoft focussed developers, so I spend some time summarizing the requirements more detailled:)).

    You can download the presentation and samples here...

    Maybe some of you know this for a longer period of time, already. But for me it was just new - a plug-in for Mozilla Firefox that allows you to install .NET Framework ClickOnce applications. That's a nice tool...

    You can find it here...

    Last week in our team JourFixe we had a short presentation from Barbara, event owner of the Big>Days 2007 road show, on statistics and event evaluations. I am really very happy that - in terms of session and presentaiton quality - the four top-sessions are from the developer sessions Max put together and organized for our tracks - congratulations:)

    The first three sessions in the ranking are mine... which makes me really happy as well as putting them together was very stressful:) here are the evaluations of my sessions (1 = best, 9 = worst):

    Office Developer Demo (Restaurant Community): 1,44
    Office SharePoint Server (Excel & InfoPath Forms Services): 1,47
    Web Designer meets Web Developer: 1,70

    The biggest surprise for me was the InfoPath and Excel session as I did not have many code in that session. But maybe the things I addressed there are solving many common problems out there:)

    It was definitely a great road show and I really enjoyed it... once again, but this time with a new and great track owner. Congrats for cool developer tracks, Max:)

    Important: if you have any feedback to the sessions and to our content in general, please contact Max or me through our blogs! This is much more important than the numbers above:))

    Finally it's out - the successor of Smart Client Software Factory and Composite UI Application Block is released in a first CTP. Since September 2006 I've been waiting for that moment - that was the time when I saw the first version of Acropolis and it's little :) Domain Specific Language in action for the first time.

    You'll find more information on the following blogs (including a statement of the Patterns & Practices Team which will release a migration guidance for CAB/SCSF based applications to Acropolis).

    imagehttp://blogs.msdn.com/dphill/archive/2007/06/05/introducing-acropolis.aspx

    http://blogs.msdn.com/gblock/archive/2007/06/06/acropolis-the-future-of-smart-client.aspx

    http://windowsclient.net/Acropolis/Default.aspx

    http://www.microsoft.com/downloads/details.aspx?FamilyId=72386CE5-F206-4D5C-AB09-413B5F31F935&displaylang=en

    For all of you using CAB/SCSF right now or planning a composite Smart Client which needs to be finished soon: don't get nervous, the final release of Acropolis will take some time for sure:)

    My personal opinion is: if you need such a framework now, then use CAB/SCSF before writing your own framework. Second: no one will force you to upgrade to Acropolis. And third: if you want to upgrade to Acropolis, the Microsoft Patterns & Practices Team will provide a step-by-step upgrade guidance.

    An official statement from the Patterns & Practices Team has been released, already, where they state, that they will continue support CAB/SCSF and that they will provide an upgrade guidance created together with the Acropolis team here...

    For me it means in any case - I'll start working with Acropolis right now and if you need someone to discuss I'll be available:)

    Yesterday Andreas and I delivered a developer-workshop on the ECMA Office Open XML File Formats together with Austria PRO, one of Austria's leading standardization organization in cooperation with Austria's chamber of economics, and the Technical University Vienna. Thanks to Univ. Prof. Christian Huemer from Austria PRO and TU Vienna and to Gerhard Göschl from Microsoft Austria who organized the workshop.

    Andreas from our team and I did a number of in-depth presentations and demos on Office Open XML including the following topics:

    • Architecture of the ECMA Office Open XML File Formats
    • Inside the System.IO.Packaging API for programmatic access to files
    • Interoperability with other platforms such as Java (incl. demo:))
    • WordML and solution development with Word and Custom XML islands
    • SpreadSheetML and solution development with Excel File Formats
    • Short overview on XSL/T and ECMA Office Open XML

    You can download my demos here - slide-material is published on our team-blog, already and Andreas' demos are available on Andreas' blog soon as well. It was a cool workshop and I definitely enjoyed it...

    Last weekend two of my best friends got married within a wonderful wedding in Altenburg close to Horn... the Austrian's would say "in the good old wood quarters". Tanja and Dominik finally took the challenge for the...most beautiful project we can start in our life:)

    Tanja and DominikYou might wonder why I am adding a post like this to my geeky web blog and wonder how a wedding fits into this blog which is mostly just a view on Microsoft's world and technologies and experience from sessions, conferences and all that stuff... well maybe more than you think because alongside my parents and grand parents Dominik and Tanja are the most important persons in my life... and without knowing them I wouldn't where I am, today.

    I still remeber as it just was yesterday although it is nearly a decade ago - about a year after we finished our college in summer Dominik was the one asking me weather I want to join a company based in lower Austria focussed on banking projects. In that summer I signed the contract and some time later that brought me to one of the most interesting and important projects in my life. This project at the Austrian Nations bank layed the foundation for the ways I am thinking today. I met some of the key people for my professional career and although it took some time and some projects before I joined Microsoft, this question from Dominik and the decision to join this company (called Langsteiner EDV) was the very first step for anything that happened afterwards. A long journey took us all from then to now... many things changed in the meantime, everyone of us toke many completly different challenges - and we took them all together.

    Some time ago Harald (our manager) & I discussed a little bit on our work and he asked where I take the power for doing so many different things... and this is the answer - it is my family and especially my friends giving me that power. Tanja and Dominik played a key role in helping me to find my ways for many things done so far - especially those which have to do nothing with work and which are even harder to manage:)... many Sunday evenings where we talked about the feelings that are not as easy to control as a project... I think they know what I am writing about:)). And the most important thing: together with all our friends they always helped me to "logoff" sometimes and forget about work and technologies. And... all of them played an important role at their wedding - the "best man" Edi, the masters of photographing Michi&Michi, the dailyWedding publishers Ralph and Gerlinde (maid of honor for Tanja)... they where all there and together we are just a fabulous team! I'll never ever miss one of them... Finally on such weekends I get remembered that there are more important things than work and technology, although I love my job and do it with all the passion I have. That's what I got reminded to when being at Tanja & Dominiks wedding. Maybe some of you know and understand now, why I am still here in Austria and not across the ocean, yet (just a few who are reading this might know what I am talking about)...

    Tanja, Dominik - I wish you all the best with all my heart and I am looking forward to even more great moments with you together!

    Mario

    During the break here I tried to search the web for mszCool - just for curiosity;) There are a couple of interesting links. I am especially proud that the last paper I've written in the meantime appears at the BPM architect and SOA business analyst blogs, already. Hopefully that's a good sign...

    http://soabusinessanalyst.com/2007/02/11/my-pointofview-on-microsofts-strategies-around-service-orientation-bpm-and-esb.aspx
    http://bpmarchitect.com/2007/02/11/my-pointofview-on-microsofts-strategies-around-service-orientation-bpm-and-esb--mario-szpusztra.aspx

    And a list of cool VSTO blog articles where one of mine is available as well;)
    http://msdn2.microsoft.com/en-us/library/aa537189(office.11).aspx

    Today I spend nearly the whole day at the Global Knowledge Take Off Developer event organized by Global Knowledge and Christian Nagel. I had the honor to deliver today's key note as well as foundation-session on Windows Communication Foundation (WCF).

    In the key note I tried to build the bridge from current market trends such as Service Orientation, BPM, Web 2.0, Generation "U" and S+S to our Connected Systems stack. The reason for doing so is, that Connected System addresses parts of these market trends and with .NET 3.0 we address 4 of the 5 parts of our Connected Systems "technology model". This all is - in my opinion - to address parts of the trends we are currently faced with on the market (Web 2.0 -> longtail -> importance of consumer orientation -> user experience -> WPF as a part of user experience).

    As the WCF session was just 45 min. long (okay, it took me 55 min.;) I had the chance to cover the basics around WCF's architecture and the ABCs, only. In general I think the event went very well...

    I really hope the attendees liked my presentations (two very contrary ones - one very abstract and conceptual and the other one pure fashioned coding;).

    You can finde the slides for download here and my WCF demo for download here...

    The last three weeks we primarily spend with our official launch tour for Windows Vista and 2007 Microsoft Office system for IT professionals, developers and architects traveling through the whole country.

    Now right back again and just a couple of ours from the next .NET 3.0 event away (Global Knowledges and Christian Nagel's Take Off Developer Day) I found a slot for uploading my demos and slides. You find the links for downloading all the demos and slides in this blog post!

    Web Developer meets Web Designer
    Slides Download, Demos Download
    This session really demonstrated how you can design web sites with Microsoft Expression Web and then add value to web sites by developing add-ons using Visual Studio 2005 by using Microsoft ASP.NET 2.0 AJAX Extensions and the AJAX Control Toolkit.

    Demo Requirements: All you need for running this demo is having Visual Studio 2005 or Microsoft Expression Web and SQL Server 2005 Express Edition installed as I've modified the demo to use file-based database access.

    Developing Solutions using InfoPath Forms Services and Excel Server Services
    Slides Download, Demos Download
    Have you ever tried running Excel in a clustered server environment? No!? That's something you really can do now with Excel Services. It allows you running complex Excel calculations in a clustered environment and integrate logic modelled in Excel into custom applications by using the Excel Web Services interface. Furthermore you can make Excel Sheets available in the browser using the new Excel Web Access - this time based on pure-fashioned ASP.NET 2.0 web sites (and not the old ActiveX stuff anymore!!). Browser based access is possible with InfoPath Forms Services as well.

    Demo Requirements: To run the demos of this session you need a developer machine running Microsoft Office InfoPath 2007 client, Excel 2007 client, Windows SharePoint Services v3 and Microsoft Office SharePoint Server 2007 incl. InfoPath Forms Services and Excel Services installed. I've used the single-box installation mode of Office SharePoint Server with the integrated SQL Server 2005 Express Edition - that's the easiest way for getting a demo environment running without the need of digging into the details of configuring SharePoint, too much. I've furthermore created a separate site collection running on http://localhost:9091 to separate the things from the demo site created by default.

    Restaurant Community in 70 min. - Demo-Only Session
    Slides Download, Demos Download
    As the last fifth session in the Office track of the day was fairly late I did not want to bother any attendees with PowerPointTerrorismExtended (pptx:)) anymore. Therefore I decided we take parts of the things you learned throughout the day in the Office Developer track to develop a restaurant community solution. In the demo-downloads I've also included the SharePoint Site Template to give you a chance to recover the SharePoint lists and libraries I've created during the session, quickly. You just need to upload the RestaurantsCommunity.stp file to your site templates and then you can create new sites of type "RestaurantCommunity" on your own SharePoint instance.

    Demo Requirements: To run the demos of this session you need a developer machine running Microsoft Office InfoPath 2007 client, Microsoft Word 2007, Visual Studio 2005 Tools for Office Second Edition, Windows SharePoint Services v3 and Microsoft Office SharePoint Server 2007 incl. InfoPath Forms Services installed. I've used the single-box installation mode of Office SharePoint Server with the integrated SQL Server 2005 Express Edition. I've furthermore created a separate site collection running on http://localhost:9091 to separate the things from the demo site created by default. Therefore for getting the Word Add-In working you need to update the URLs in the application configuration file using the Properties-Folder of the solution explorer of Visual Studio 2005 within the add-in project.