Creating a Master-Detail report

A Master-Detail report renders data from a hierarchical data source such as related tables or nested objects.

Nested related tables are recognized automatically and merged based on the merge block names in the template. The following report lists all sales and the sold products for each sale. Each sale is rendered on a new page in the final report.

Demo

Template (before Merge):

Preview (after Merge):


Merge blocks can be nested in unlimited number of levels. Infinite loops are recognized and prevented automatically. Merge blocks can consist of everything: A word, a paragraph, a table row, complete tables or several pages.

Sources

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace tx_aspnet_samples.Reporting
{
    public partial class masterdetail : System.Web.UI.Page
    {
        private TXTextControl.DocumentServer.MailMerge mailMerge1;
        private TXTextControl.ServerTextControl serverTextControl1;
        private TXTextControl.DocumentServer.DocumentController documentController1;
        private TXTextControl.ServerTextControl serverTextControl2;
        private TXTextControl.DocumentServer.DocumentController documentController2;
        private TXTextControl.ServerTextControl serverTextControl3;
        private System.ComponentModel.IContainer components;
    
        protected void Page_Load(object sender, EventArgs e)
        {
            InitializeComponent();
            DocumentViewer1.DocumentController = documentController1;
            DocumentViewer2.DocumentController = documentController2;
            DocumentViewer1.LoadDocument(Server.MapPath("/documents/template_master.docx"),
                TXTextControl.DocumentServer.FileFormat.WordprocessingML);
        }

        protected void btn_CreateReport_Click(object sender, EventArgs e)
        {
            // open the sample XML file and create DataSet
            DataSet ds = new DataSet();
            ds.ReadXml(Server.MapPath("/datasource/sample_db.xml"), XmlReadMode.Auto);

            // load the template
            mailMerge1.LoadTemplate(Server.MapPath("/documents/template_master.docx"), 
                TXTextControl.DocumentServer.FileFormat.WordprocessingML);
            
            // pass the master table to start the merge process
            mailMerge1.Merge(ds.Tables["Sales_SalesOrderHeader"]);

            byte[] data;

            // save the document to memory as PDF
            mailMerge1.SaveDocumentToMemory(out data,
                TXTextControl.BinaryStreamType.InternalUnicodeFormat, null);

            DocumentViewer2.LoadDocumentFromMemory(data, 
                TXTextControl.DocumentServer.FileFormat.InternalUnicodeFormat);

            // store document for a later download
            Session["data"] = data;
            btnDownloadPDF.Visible = true;
        }

        protected void btn_DownloadPDF_Click(object sender, EventArgs e)
        {
            if (Session["data"] == null)
                return;

            byte[] data;

            // convert stored document to PDF
            using (TXTextControl.ServerTextControl tx =    
                new TXTextControl.ServerTextControl())
            {
                tx.Create();
                tx.Load((byte[])Session["data"],
                    TXTextControl.BinaryStreamType.InternalUnicodeFormat);
                tx.Save(out data, TXTextControl.BinaryStreamType.AdobePDF);
            }

            // return the document to the browser for download
            Response.Clear();
            Response.AddHeader("content-disposition",
                String.Format("attachment;filename={0}", "created_by_txtextcontrol.pdf"));
            Response.ContentType = "application/pdf";
            Response.BinaryWrite(data);
            Response.End();
        }
    }
}

Mail Merge - Reporting