Products Technologies Demo Docs Blog Support Company

Reporting: Sorting Merge Block Rows by Column Name

The concept of Text Control Reporting is to accept pre-shaped data. That means that Text Control's MailMerge component is merging the data rows 'as-is' and data shaping, sorting or queries are done in the application business layer. Sometimes, it is required to define the order of data rows in the template. This sample shows how to realize this concept based on additional parameters in the merge block name and LINQ to sort the data rows. Let's have a look at the XML data source. It consists…

Reporting: Sorting Merge Block Rows by Column Name

The concept of Text Control Reporting is to accept pre-shaped data. That means that Text Control's MailMerge component is merging the data rows 'as-is' and data shaping, sorting or queries are done in the application business layer.

Sometimes, it is required to define the order of data rows in the template. This sample shows how to realize this concept based on additional parameters in the merge block name and LINQ to sort the data rows.

Let's have a look at the XML data source. It consists of 2 simple tables: company and product and a relation between those tables.

Data source

The template simply consists of a single merge block with the name product. Without any sorting, the results would look like this:

Merge results without sorting

In order to enable sorting, we simply introduce a new parameter to the merge block name:

[BlockName];orderby,[ColumnName],ASC|DESC

For example:

product;orderby,name,ASC

The sample code shows how to detect and parse these merge blocks in the template and how to replace the rows of the relevant DataTables with sorted DataRows.

The class DataSelector is parsing the template in order to sort the given data source. The property Template returns the changed template which is loaded into a MailMerge instance using LoadTemplateFromMemory. Finally, the document is merged with the sorted DataSet:

private string sDataSource = "data.xml";
private string sTemplateFile = "template.tx";

private void mergeToolStripMenuItem_Click(object sender, EventArgs e)
{
    // convert the XML file to a .NET DataSet
    DataSet ds = new DataSet();
    ds.ReadXml(sDataSource, XmlReadMode.Auto);

    // create a new DataSelector instance
    DataSelector selector = new DataSelector(ds,
        File.ReadAllBytes(sTemplateFile));

    // load the modified template
    mailMerge1.LoadTemplateFromMemory(selector.Template,
        TXTextControl.DocumentServer.FileFormat.InternalUnicodeFormat);
    // merge the template with the new, sorted data source
    mailMerge1.Merge(ds.Tables[0]);

    // copy the merged document into a visible TextControl object
    byte[] data = null;
    mailMerge1.SaveDocumentToMemory(out data,
        TXTextControl.BinaryStreamType.InternalUnicodeFormat, null);
    textControl1.Load(data,
        TXTextControl.BinaryStreamType.InternalUnicodeFormat);
}

The DataSelector class loops through all merge blocks in a given template to check for specific keywords. A temporary ServerTextControl instance is used to recognize blocks with the sorting keyword 'ORDERBY'. If such tagged merge blocks are found, the parameters are parsed, stored and the block name is reset to the original block name.

Finally, LINQ is used to sort the data rows by the given column name. The resulting data rows are replaced in the specific DataTable. The DataTable won't be replaced completely to keep existing data relations and constraints.

// DataSelector
// description: This class loops through all merge blocks in a given template to check
// for sort keywords. The given referenced DataSet will be sorted.
//
// Parameters: dataSet of type DataSet, template as a byte[] array in the InternalUnicodeFormat
public class DataSelector
{
    public byte[] Template { get; set; }

    public DataSelector(DataSet dataSet, byte[] template)
    {
        // use a temporary ServerTextControl instance to recognize blocks with
        // the sorting keyword 'ORDERBY'
        using (TXTextControl.ServerTextControl tx =
            new TXTextControl.ServerTextControl())
        {
            tx.Create();
            // load the template
            tx.Load(template, TXTextControl.BinaryStreamType.InternalUnicodeFormat);

            // create a list of merge blocks
            List<MergeBlock> lMergeBlocks = MergeBlock.GetMergeBlocks(MergeBlock.GetBlockMarkersOrdered(tx), tx);

            // loop through all merge blocks to check whether they
            // have a sorting parameter
            foreach (MergeBlock mergeBlock in lMergeBlocks)
            {
                string sBlockName = mergeBlock.StartMarker.TargetName;

                // check for the unique sorting parameter
                if (sBlockName.ToUpper().Contains("ORDERBY") == false)
                    continue;

                // create a new SortedBlock object to store parameter
                SortedBlock block = new SortedBlock(sBlockName);

                // remove the sorting parameter from the block name
                // so that MailMerge can find the matching data in the data source
                mergeBlock.StartMarker.TargetName = block.Name;
                mergeBlock.EndMarker.TargetName = "BlockEnd_" + mergeBlock.Name;

                if (dataSet.Tables.Contains(mergeBlock.Name) == false)
                    continue;

                // get all DataRows using LINQ
                var query = from product in dataSet.Tables[mergeBlock.Name].AsEnumerable()
                            select product;

                // create a new DataTable with the sorted DataRows
                DataTable dtBoundTable = (block.SortType == SortType.ASC) ?
                    query.OrderBy(product => product.Field<String>(block.ColumnName)).CopyToDataTable() :
                    query.OrderByDescending(product => product.Field<String>(block.ColumnName)).CopyToDataTable();

                // remove original rows and replace with sorted rows
                dataSet.Tables[mergeBlock.Name].Rows.Clear();
                dataSet.Tables[mergeBlock.Name].Merge(dtBoundTable);

                dtBoundTable.Dispose();
            }

            // save the template
            byte[] data = null;
            tx.Save(out data, TXTextControl.BinaryStreamType.InternalUnicodeFormat);
            this.Template = data;
        }
    }
}

Let's do two examples. The merge block name contains the following string:

product;orderby,name,ASC

The ascending sorted results are shown below:

Results with ASC sorting

Descending sorting results:

Results with DESC sorting

LINQ is used to sort the results to show the flexibility. You can customize this process with your own selectors. This shows the flexibility and the power of the MailMerge class. Give it a try to download the sample project directly from GitHub.

Download the sample from GitHub and test it on your own.

Stay in the loop!

Subscribe to the newsletter to receive the latest updates.

GitHub

Download and Fork This Sample on GitHub

We proudly host our sample code on github.com/TextControl.

Please fork and contribute.

Download ZIP

Open on GitHub

Open in Visual Studio

Requirements for this sample

  • Visual Studio 2012 or better
  • TX Text Control .NET for Windows Forms (trial sufficient)

Reporting

The Text Control Reporting Framework combines powerful reporting features with an easy-to-use, MS Word compatible word processor. Users can create documents and templates using ordinary Microsoft Word skills. The Reporting Framework is included in all .NET based TX Text Control products including ASP.NET, Windows Forms and WPF.

See Reporting products

Related Posts

ReportingWindows FormsGitHub

Windows Forms: Printing Multiple Pages Per Sheet

This sample project implements the class MultipagePrintDocument that inherits from System.Drawing.Printing.PrintDocument to print multiple pages of a document per sheet. The constructor of…


ReportingWindows FormsGitHub

Inserting Watermark Images to All Pages Dynamically

This sample project shows how to create and insert a watermark image on all pages dynamically. Image objects have the Name property to store additional string information with an image. This…


ReportingWindows FormsGitHub

MailMerge: Conditional INCLUDETEXT Fields

In documents and reports, often sentences or an appendix are added only under specific conditions. The Text Control reporting engine MailMerge provides the concept of INCLUDETEXT fields to include…


Windows FormsGetting StartedTutorial

Windows Forms Tutorial: Create Your First Windows Forms C# Application

This tutorial shows how to create your first Windows Forms application with C# using TX Text Control .NET for Windows Forms in Visual Studio 2022.


ActiveXASP.NETReporting

TX Text Control 32.0 Has Been Released

We are pleased to announce the immediate availability of TX Text Control 32.0 for all platforms including ASP.NET, Windows Forms, WPF and ActiveX.