TX Text Control .NET for Windows Forms

Merge Fields

Topic

A merge field is more than a name in the template: it carries a format for numbers or dates, text that appears before and after its value and the case of its letters. Users set these options in the Merge Field Options dialog box of the Reporting tab, and your code sets them with the MergeField class. The sections below cover both, add date fields, decide what fields without data show, merge formatted HTML and fill form fields.

Inserting a first merge field with the ribbon and in code is part of the Getting Started tutorial Create a Template, and finding fields without data is part of Merge Data into Fields. The examples below use the same template, invoice.docx, shown in a TextControl named textControl1.

The MergeField Class

A merge field is an MS Word MERGEFIELD field, stored in the document as an ApplicationField. The MergeField class of the TXTextControl.DocumentServer.Fields namespace is an adapter for such a field: the constructor without parameters creates a new field, and the constructor with an ApplicationField wraps an existing one, so that changes to the adapter change the field in the document.

PropertySetsIn the Merge Field Options dialog
NameThe name that is matched with the dataName
TextThe text the field shows until it's mergedText
TextFormatThe case of the letters: Uppercase, Lowercase, FirstCapital, TitleCase or NoneFormat
TextBefore, TextAfterText inserted before and after the valueText to be inserted before, Text to be inserted after
DateTimeFormatA format for dates and times, the MS Word switch \@Date / time format
NumericFormatA format for numbers, the MS Word switch \#Numeric format
PreserveFormattingA flag that MailMerge itself doesn't usePreserve formatting during updates
DataSourceCulture, MergeCultureThe cultures that parse the value and format it, for this field only

The formatting of the field in the template, such as its font and color, is always kept.

Insert and Edit Fields in the Reporting Tab

Once data is loaded into the Reporting tab, Select Master Table in the Data Source group chooses the table whose columns the fields come from. Insert Merge Field in the Merge Fields group then lists the columns of that table, and a click inserts a field at the input position:

The Insert Merge Field drop-down of the Reporting tab listing the columns of the table Sales_SalesOrderDetail

Merge Fields in Reporting Ribbon and Dialog Boxes describes the rest of the drop-down, including the fields of related tables.

Field Properties in the same group opens the Merge Field Options dialog box for the field at the input position. It has one control for each property of the table above:

The Merge Field Options dialog box with the field ProductID, its text, a Format drop-down, text before and after, date and time and numeric formats and the Preserve formatting during updates check box

Your code opens the same dialog box with ShowDialog of a MergeField that wraps the field at the input position, for example from a context menu of your own. Open the Dialog Boxes From Code shows the handler, which opens the options of merge fields and the other fields of a template.

Format Numbers and Dates

NumericFormat and DateTimeFormat take .NET format strings, the standard and custom numeric format strings and date and time format strings. For example, a field Price with the numeric format $#,###.00 shows the value 2.4 as $2.40, and a date field with MMMM d, yyyy shows September 1, 2026.

The currency symbol, the separators and the names of months depend on the culture that formats the value. MailMerge.MergeCulture sets it for all fields and MergeField.MergeCulture for a single field; both default to the culture of the current thread, which follows the user's regional settings. Set a culture when the documents must look the same on every computer. The blog article Format Numeric String Values with MailMerge shows the formats in use.

Set Formats on All Fields of a Template

Merge fields can be in the main text, in headers and footers and in text frames. TextControl.TextParts contains these parts, and each part has its own ApplicationFields collection. The following handler sets formats on the fields of the template in the editor, wherever they are:

using System;
using TXTextControl;
using TXTextControl.DocumentServer.Fields;
private void formatFieldsButton_Click(object sender, EventArgs e)
{
    // The main text, text frames, headers and footers
    foreach (IFormattedText textPart in textControl1.TextParts)
    {
        foreach (ApplicationField applicationField in textPart.ApplicationFields)
        {
            if (applicationField.TypeName != "MERGEFIELD")
            {
                continue;
            }

            MergeField mergeField = new MergeField(applicationField);

            switch (mergeField.Name)
            {
                case "Company":
                    mergeField.TextFormat = TextFormatOptions.Uppercase;
                    break;

                case "InvoiceNumber":
                    mergeField.TextBefore = "Invoice ";
                    break;

                case "Price":
                    mergeField.NumericFormat = "$#,##0.00";
                    break;
            }
        }
    }
}

The code works as follows:

  • Each text part implements IFormattedText, whose ApplicationFields holds the fields of that part.
  • new MergeField(applicationField) wraps each merge field, and the new values apply to the field in the document at once.
  • After the handler, the company name is merged in capital letters, the invoice number is preceded by "Invoice " and the prices of the Items block are shown with a dollar sign and two decimals. Save the template to keep the settings.

Insert Date Fields

A DATE field shows the date of the merge in a format. Users insert one with Date in the Insert Special Field drop-down of the Reporting tab and choose the format in the Date Field Options dialog box:

The Date Field Options dialog box with a list of date formats, the selected format dddd, d. MMMM yyyy and the Preserve formatting during updates check box

In code, the DateField adapter creates the field:

using System;
using System.Globalization;
using TXTextControl.DocumentServer.Fields;
private void insertDateButton_Click(object sender, EventArgs e)
{
    DateField dateField = new DateField
    {
        Format = "MMMM d, yyyy",
        Culture = CultureInfo.GetCultureInfo("en-US"),
        Date = DateTime.Now
    };

    textControl1.ApplicationFields.Add(dateField.ApplicationField);
}

Format is a standard .NET date and time format string, Culture the culture that applies it, and Date sets the date the field shows in the template. During the merge, the field receives the local date and time of the computer. MailMerge.DateFieldUtcOffset applies an offset from UTC in minutes instead; it is null by default.

Show a Text for Fields Without Data

A field whose name the data doesn't contain is removed, because RemoveEmptyFields is true by default. To show a text instead, keep such fields and change their text in the FieldMerged event, which occurs for every field, with or without data:

using System;
using System.IO;
using TXTextControl.DocumentServer;
using TXTextControl.DocumentServer.Fields;
private void mergeWithPlaceholdersButton_Click(object sender, EventArgs e)
{
    using (MailMerge mailMerge = new MailMerge
    {
        TextComponent = textControl1,
        RemoveEmptyFields = false
    })
    {
        mailMerge.FieldMerged += (s, args) =>
        {
            if (!args.Merged && args.MailMergeFieldAdapter is MergeField mergeField)
            {
                mergeField.Text = "n/a";
            }
        };

        mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
    }
}

Merged is false for a field without data, and MailMergeFieldAdapter is the field, here a MergeField whose text becomes "n/a". Add a field named Phone, which invoice.json doesn't contain, to see the text. Merge Events describes the other arguments of the event.

Merge Formatted Text

A merge field receives plain text. When the data holds formatted text, such as HTML notes, replace the merged content in FieldMerged: load the value into a ServerTextControl, save it in the internal TX Text Control format and assign the bytes to MergedField. The Preserve formatting during updates check box of the Merge Field Options dialog box is a convenient marker for such fields: MailMerge ignores it, but your handler can read it as PreserveFormatting:

mailMerge.FieldMerged += (s, args) =>
{
    // Only fields with "Preserve formatting during updates" checked
    if (args.Merged && args.MailMergeFieldAdapter is MergeField mergeField && mergeField.PreserveFormatting)
    {
        using (ServerTextControl htmlText = new ServerTextControl())
        {
            htmlText.Create();
            htmlText.Load(mergeField.Text, StringStreamType.HTMLFormat);

            byte[] data;
            htmlText.Save(out data, BinaryStreamType.InternalUnicodeFormat);
            args.MergedField = data;
        }
    }
};

With a value such as <p>Thank you for your <strong>business</strong>.</p> in the data, the merged document shows the text with business in bold instead of the HTML tags. The blog article Reporting: Merge Formatted HTML into Merge Fields introduced this technique with XML data.

Keep Merge Fields After the Merge

After a merge, a field that received data is gone: its value is ordinary text of the document. That suits a finished letter, but not a document that users edit in the TextControl and merge again later, or a preview that should still show the fields. Assign MergedField to itself in FieldMerged, and the merged fields stay fields with the merged value as their text:

using (MailMerge mailMerge = new MailMerge { TextComponent = textControl1 })
{
    // Assigning MergedField to itself keeps the merged fields in the result
    mailMerge.FieldMerged += (s, args) => args.MergedField = args.MergedField;

    mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
}

Fields without data are a separate case: RemoveEmptyFields set to false keeps them, as Show a Text for Fields Without Data does. The blog article Merging Templates with MailMerge with Different Merge Field Settings in C# compares both settings with screenshots of the results.

Merge Form Fields

Templates can also contain form fields, such as text boxes, check boxes and drop-downs that users fill in later. MailMerge.FormFieldMergeType decides what the merge does with them:

ValueForm fields
None (default)Aren't merged
PreselectKeep their controls, and their content is preselected with the data where possible; fields without a matching column stay unchanged
ReplaceAre replaced with the data, so the result contains no form fields; empty form fields are removed according to RemoveEmptyFields

The FormFieldMerged event reports each form field and whether it received data:

using (MailMerge mailMerge = new MailMerge
{
    TextComponent = textControl1,
    FormFieldMergeType = FormFieldMergeType.Preselect
})
{
    mailMerge.FormFieldMerged += (s, args) =>
    {
        if (!args.Merged)
        {
            System.Diagnostics.Debug.WriteLine($"No data for form field {args.FormField.Name}");
        }
    };

    mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
}

With Preselect, users see a form in the editor that is already filled out where the data allows it. Data that users must not change belongs in merge fields; values they may correct belong in preselected form fields. Form Fields in the Document Editor section describes the form fields themselves, and the blog article Merging Form Fields using the MailMerge Class shows a form template with both kinds of fields.

Prefixes in Field Names

A dot in a field name addresses related data: Customer.City is the value City of the nested object or related table Customer, and longer paths reach further levels. The prefix image: marks a merge field that is merged as an image, as Images and Barcodes describes.

Prefixes also work inside merge blocks, where they start at the table of the block. The sample database of TX Words has an order table, Sales_SalesOrderHeader, which is related to the order lines in Sales_SalesOrderDetail, and each order line to a product in Production_Product:

Three related tables: Sales_SalesOrderHeader connected to Sales_SalesOrderDetail by SalesOrderID, and Sales_SalesOrderDetail connected to Production_Product by ProductID.

With Sales_SalesOrderHeader as the master table, a merge block Sales_SalesOrderDetail repeats the order lines. Inside it, OrderQty, UnitPrice and LineTotal need no prefix, because they are columns of the block's own table, while Production_Product.ProductNumber and Production_Product.Name take the number and the name of the product of the current line.

A table row merge block with the fields ProductNumber, Name, OrderQty, UnitPrice and LineTotal and an image placeholder, highlighted in red.

A path can follow several relations in a row. The product photo of each order line is an image placeholder in the same block, and its name leads from the product through the table that links products and photos to the photo itself:

Production_Product.Production_ProductProductPhoto.Production_ProductPhoto.ThumbNailPhoto

Each part before the last is a related table, and ThumbNailPhoto is the column with the image data. MailMerge follows the relations by itself; you pass only the master table. The Insert Merge Field drop-down offers related tables as submenus and inserts their fields with the prefix, as Reporting Ribbon and Dialog Boxes shows.

The merged invoice shows the order, its lines with the product numbers and names and the product photos, all filled from the related tables:

A merged payment invoice with the sender and customer addresses, two order lines with product code, name, quantity, prices and a photo of a bike, and the totals below.

Next Steps