Using Word to Create Master-Detail Reports for LightSwitch (or Silverlight)

A while back a posted how to use Word to create simple reports for LightSwitch by passing in an entity (like Customer) and using Word Content Controls to lay out content in a template and bind them to XML inside the document. If you missed it: Using Microsoft Word to Create Reports For LightSwitch (or Silverlight)

In the comment thread to that post there were requests on how to create master detail reports, for instance an Order and OrderDetails. In my last post I released a sample that shows one way to achieve this. The code is part of a larger LightSwitch sample application: Contoso Construction - LightSwitch Advanced Development Sample. In that sample I use the same technique in the post referenced above to add a helper class and Word document template to the client project and bind XML to content controls. The only difference to achieve the master-detail formatting is in the layout of the content controls and the code that generates the XML.

In the Contoso Construction sample, we have a parent “Project” table that has many “ProjectMaterials”. If you open the project screen you will see a button at the top that allows you to generate a project status report which displays fields from the Customer, Project and all the ProjectMaterials used on the construction project.

image

Project has a one-to-many relationship to ProjectMaterials so we could have one or many lines of materials to display. One way to do this is to lay out a single content control that will contain multiple lines like so:

image

So in this case I have four content controls representing the four fields I want to display off of the ProjectMaterial entity. Then all you need to do when generating the XML is to iterate the collection of children and put line breaks between them. You can do this easily using the Join method which takes an array and creates a string representation of the contents with a delimiter you specify, in my case I’m using the carriage return (vbCr). So in the MyReportHelper class we have code like so, paying particular attention to how I’m generating the <projectmaterials> node:

 Public Shared Sub RunProjectStatusReport(ByVal project As Project)
    If AutomationFactory.IsAvailable Then
        Try

            'Create the XML data from our entity properties.
            ' Project materials content controls on the Word template are set to allow carriage 
            ' returns so we can easily display as many line items as we need                    ' 
            Dim myXML = <customer>
                <fullname><%= project.Customer.FullName %></fullname>
                <homephone><%= project.Customer.HomePhone %></homephone>
                <mobilephone><%= project.Customer.MobilePhone %></mobilephone>
                <email><%= project.Customer.Email %></email>
                <fulladdress><%= project.Customer.FullAddress %></fulladdress>
                    <project>
                        <projectname><%= project.ProjectName %></projectname>
                        <startdate><%= project.StartDate.ToShortDateString %></startdate>
                        <estimatedenddate><%= project.EstmatedEndDate.ToShortDateString %></estimatedenddate>
                        <originalestimate><%= Format(project.OriginalEstimate, "c2") %></originalestimate>
                        <labor><%= Format(project.Labor, "c2") %></labor>
                        <totalcost><%= Format(project.TotalCost, "c2") %></totalcost>
                        <notes><%= project.Notes %></notes>
                        <projectmaterials>
                            <summary><%= Join((From m In project.ProjectMaterials
                             Select m.Summary).ToArray, vbCr) %></summary>
                            <quantity><%= Join((From m In project.ProjectMaterials
                             Select CStr(m.Quantity)).ToArray, vbCr) %></quantity>
                            <price><%= Join((From m In project.ProjectMaterials
                             Select Format(m.Price, "c2")).ToArray, vbCr) %></price>
                            <itemtotal><%= Join((From m In project.ProjectMaterials
                             Select Format(m.ItemTotal, "c2")).ToArray, vbCr) %></itemtotal>
                         </projectmaterials>
                      </project>
                  </customer>

          Using word = AutomationFactory.CreateObject("Word.Application")
            'The report template already has content controls bound to XML inside. 
            ' Look in the ClientGenerated project to view the Word template.
            Dim resourceInfo = System.Windows.Application.GetResourceStream(
                               New Uri("ProjectStatus.docx", UriKind.Relative))
            Dim fileName = CopyStreamToTempFile(resourceInfo.Stream, ".docx")

            Dim doc = word.Documents.Open(fileName)
            'Grab the existing bound custom XML in the doc
            Dim customXMLPart = doc.CustomXMLParts("urn:microsoft:contoso:projectstatus")

            Dim all = customXMLPart.SelectSingleNode("//*")
            Dim replaceNode = customXMLPart.SelectSingleNode("/ns0:root[1]/customer[1]")

            'replace the <customer> node in the existing custom XML with this new data
            all.ReplaceChildSubtree(myXML.ToString, replaceNode)

            word.Visible = True

          End Using
        Catch ex As Exception
          Throw New InvalidOperationException("Failed to create project status report.", ex)
        End Try
    End If
End Sub

Hope this helps. For details on how to create the Word templates, bind them to XML and how to create the MyReportHelper class please read Using Microsoft Word to Create Reports For LightSwitch (or Silverlight).

Enjoy!