TX Text Control .NET for Windows Forms

Overview

Topic

Reporting in TX Text Control .NET for Windows Forms joins a word processor and a merge engine in one form: users design a template in the TextControl with the Reporting ribbon tab, and the MailMerge component fills it with data, either in the visible editor or in the background. The concepts below apply to every template, whatever the data: how the layout grows with the data, how the data is shaped, where the merge runs and how many documents it produces.

The Mail Merge tutorials in Getting Started build a first invoice template and merge it, and the MailMerge page of the Overview introduces the component. The examples here use the same files: the template invoice.docx and the data invoice.json with the fields Company, Name and InvoiceNumber and the list Items.

The Reporting Model

A template is an ordinary document. There are no fixed report bands: users design it with the features of a word processor, such as tables, page columns, images and headers and footers, and the placeholders keep the formatting they are given.

  • The text flows. When merged content makes a table or a paragraph longer, the lines and pages break again, and the table continues on the next page.
  • Templates hold names, not connections. A template contains the names of merge fields and merge blocks. The data comes from your code or, for databases, from a separate data source configuration file, so one template works with any data of the same structure.
  • Merge fields keep their formatting. The font, size and color of a field in the template are those of the merged value.

The following picture shows an invoice merged from such a template in the TX Words Reporting sample: the table of items has grown by one row per product.

A merged invoice in TX Words with a table of six products, their quantities, prices and images

Pre-Shaped Data

MailMerge doesn't query a database while it merges data that your code passes. You prepare the data in the hierarchy the document needs and pass it to a merge method; Text Control calls this pre-shaped data. The data can come from any number of sources, as long as your code brings it into this shape first. The one exception is a data source configuration file: with it, Merge() connects to the database the file names and reads the master table and its related tables itself.

Internally, the hierarchy becomes tables:

In the dataIn the template
A property of the root object, a column of the master tableA merge field of the same name, such as Company
A nested object, a related table with one rowA merge field with dot notation, such as Customer.City
An array, a collection or a related tableA merge block of the same name, such as Items, repeated for each record

The records you pass form the master table. The template is merged once for each record, so an array of 100 invoices creates 100 invoices. Data Sources lists the kinds of data MailMerge accepts.

Where the Merge Runs

MailMerge holds no document. Its TextComponent property connects it to the control that holds the template and, after the merge, the result:

Connected toThe template isThe result
The TextControl of the formThe document in the editorReplaces the template in the editor, where the user sees it
A ServerTextControlLoaded by your code, not visibleStays in the ServerTextControl until your code saves or shows it

Merging in the editor is the natural choice for a preview or for a user who edits the merged letter. Merging in a ServerTextControl leaves the template in the editor untouched and suits batches of documents.

In the Windows Forms designer, MailMerge is a component of the TX Text Control 34.0 toolbox tab. Dropped on a form, it appears in the component tray below the form:

The Visual Studio designer with a TextControl on Form1, the Toolbox open at the TX Text Control components and mailMerge1 in the component tray

Select the component and choose the TextControl in the drop-down of its TextComponent property in the Properties window:

The Properties window of mailMerge1 with the TextComponent drop-down open and textControl1 selected

The page size and margins of the TextControl or ServerTextControl connected through TextComponent are used for the merged document. Set UseTemplateFormat to true to keep those of the template instead; the default is false.

What a Merge Processes

A merge fills more than merge fields:

ElementWhat happensDescribed in
MERGEFIELD fieldsReplaced with the value of the same nameMerge Fields
DATE fieldsFilled with the current date in the field's formatMerge Fields
IF fieldsInsert one of two texts, depending on a comparison with the dataConditional Content
NEXT and NEXTIF fieldsContinue with the next record in the same document, always or on a condition, for example on a sheet of labelsNEXT and NEXTIF Fields, the blog article Sneak Peek Reporting X10: Creating Labels with NEXT Fields
INCLUDETEXT fieldsReplaced with another document, a subtemplateSubtemplates
Merge blocksRepeated for each record of a list, also nestedMerge Blocks, Nested Blocks and Master-Detail Reports
Image and barcode placeholdersFilled with an image or encode a valueImages and Barcodes
Form fieldsLeft alone, preselected or replaced, depending on FormFieldMergeTypeMerge Fields

Fields in headers and footers are merged as well.

Create One Document or One per Record

MergeJsonData, MergeObjects and Merge have a second parameter, append. With true, the default, all records are merged into one document. With false, each record becomes a document of its own, and the connected control holds only the last one when the merge ends.

To keep every document, handle the DataRowMerged event: its MergedRow property holds the document of the record in the internal TX Text Control format. The following Click handler of a button named splitButton saves one PDF per invoice of invoice.json:

using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using TXTextControl;
using TXTextControl.DocumentServer;
private void splitButton_Click(object sender, EventArgs e)
{
    List<byte[]> documents = new List<byte[]>();

    using (ServerTextControl tx = new ServerTextControl())
    {
        tx.Create();

        LoadSettings loadSettings = new LoadSettings
        {
            ApplicationFieldFormat = ApplicationFieldFormat.MSWord,
            LoadSubTextParts = true
        };

        tx.Load("invoice.docx", StreamType.WordprocessingML, loadSettings);

        using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
        {
            // Collect the document of each record
            mailMerge.DataRowMerged += (s, args) => documents.Add(args.MergedRow);

            // false: a separate document for each record
            mailMerge.MergeJsonData(File.ReadAllText("invoice.json"), false);
        }
    }

    for (int i = 0; i < documents.Count; i++)
    {
        using (ServerTextControl document = new ServerTextControl())
        {
            document.Create();
            document.Load(documents[i], BinaryStreamType.InternalUnicodeFormat);
            document.Save($"invoice-{i + 1}.pdf", StreamType.AdobePDF);
        }
    }

    MessageBox.Show(this, $"{documents.Count} invoices created");
}

The code works as follows:

  • The LoadSettings import the MS Word fields and the merge blocks of the DOCX template, as in Merge Data into Fields.
  • DataRowMerged occurs once per record, and the handler keeps each byte array.
  • Each byte array is loaded into a new ServerTextControl and saved as invoice-1.pdf, invoice-2.pdf and so on.

invoice.json holds one invoice, so the handler creates one PDF. Add a second object with the same properties to its array to see a second file.

Load, Save and Print With MailMerge

MailMerge can also load the template and save or print the result itself. The following handler merges invoice.docx in a ServerTextControl, saves the result as PDF and prints it on the printer the user chooses:

using System;
using System.Drawing.Printing;
using System.IO;
using System.Windows.Forms;
using TXTextControl;
using TXTextControl.DocumentServer;
private void printButton_Click(object sender, EventArgs e)
{
    using (ServerTextControl tx = new ServerTextControl())
    {
        tx.Create();

        using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
        {
            if (!mailMerge.LoadTemplate("invoice.docx", FileFormat.WordprocessingML))
            {
                MessageBox.Show(this, "The template could not be loaded.");
                return;
            }

            mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
            mailMerge.SaveDocument("invoice.pdf", StreamType.AdobePDF, new SaveSettings());

            // Let the user choose the printer
            using (PrintDialog printDialog = new PrintDialog { Document = new PrintDocument() })
            {
                if (printDialog.ShowDialog(this) == DialogResult.OK)
                {
                    mailMerge.Print(printDialog.Document);
                }
            }
        }
    }
}
  • LoadTemplate returns false when the template can't be loaded. Its format is a value of FileFormat: MSWord, RichTextFormat, WordprocessingML or InternalUnicodeFormat. LoadTemplateFromMemory loads a template from memory, and the TemplateFile property, which you can also set in the Properties window, names a file whose format is deduced from its extension.
  • SaveDocument saves the merged document to a file, and SaveDocumentToMemory to a byte array or a string.
  • Print prints the merged document with the settings of a PrintDocument, such as the printer, the page range and the number of copies. A second overload takes the document name, the printer name, the number of copies and whether to collate.

LoadTemplate takes no LoadSettings. To decide how the fields and blocks of a DOCX template are imported, load it with Load and LoadSettings, as in the previous example.

Follow the Merge With Events

MailMerge raises events while it merges, for example FieldMerged for each field, BlockRowMerged for each row of a merge block and DataRowMerged for each record. Your handlers can check the data, change the merged content or collect the results. Merge Events describes them all.

Next Steps