TX Text Control .NET Server

Styling

Topic

In a mail merge, a document gets its look from the template: fonts, colors, table borders and page breaks stay as designed while the data is merged. Formatting that depends on the data, such as alternating row colors or a highlighted amount, comes from rules in the template or from a few lines of code in the events of MailMerge.

Number and date formats of merge fields and formatted text from the data are covered in Merge Fields. The formatting API itself is described in Text, Paragraphs and Tables. Every program merges the invoice data of Simple Mail Merge, with the LineItems array of Merge Blocks added, into a template.docx that also has a LineItems table.

What the Merge Keeps

TX Text Control keeps formatting in the template and merges plain values into it:

  • Merge fields keep their formatting, such as font, size and color. The merged value looks like the field in the template.
  • Merge blocks are cloned with their formatting and styles for each record, including the paragraph settings. With PageBreakBefore set for the first paragraph of a block, each record starts on a new page.
  • IF fields insert their text with the formatting of the field.
  • Table header rows are repeated on each page when a merged table continues on the next page.

Design paragraph styles, table formats and page breaks in the template, and keep the data free of formatting. Template Design collects further advice for templates.

Alternate Row Colors in the Template

Alternating row colors need no code. Make the merge block two table rows with the same merge fields and different background colors, and put a NEXT field at the end of the first row: the field advances to the next record, so the second row shows the following record in its own color. The online demo Alternating row colors shows such a template, and the colors stay editable like any other formatting of the template.

The blog article Reporting: Removing Empty Table Rows describes how the second row of the last block can stay empty when the number of records is odd, and how to remove it.

Alternate Row Colors in Code

The BlockRowMerged event occurs after each row of a merge block and gives access to the merged row as a document in the TX Text Control format. Load it into a second ServerTextControl, change its formatting and assign it back. The following program colors every second row of the LineItems block:

using TXTextControl;
using TXTextControl.DocumentServer;

string jsonData = File.ReadAllText("data.json");

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

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

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

    int rowCount = 0;

    using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
    {
        mailMerge.BlockRowMerged += (sender, e) =>
        {
            if (e.MergeBlockName != "LineItems")
            {
                return;
            }

            // Color every second row of the block
            rowCount++;

            if (rowCount % 2 != 0)
            {
                return;
            }

            using (ServerTextControl rowText = new ServerTextControl())
            {
                rowText.Create();
                rowText.Load(e.MergedBlockRow, BinaryStreamType.InternalUnicodeFormat);

                foreach (Table table in rowText.Tables)
                {
                    foreach (TableRow row in table.Rows)
                    {
                        TableCellFormat cellFormat = row.CellFormat;
                        cellFormat.BackColor = System.Drawing.Color.LightGray;
                        row.CellFormat = cellFormat;
                    }
                }

                byte[] data;
                rowText.Save(out data, BinaryStreamType.InternalUnicodeFormat);
                e.MergedBlockRow = data;
            }
        };

        mailMerge.MergeJsonData(jsonData);
    }

    tx.Save("alternating-rows.pdf", StreamType.AdobePDF);
}

The code works as follows:

  • MergeBlockName tells which block the row belongs to, so other blocks stay unchanged.
  • MergedBlockRow contains the merged row as a byte array, which rowText loads.
  • TableRow.CellFormat is read, changed and assigned back, which sets the background color of all cells of the row.
  • The changed row is saved and assigned to MergedBlockRow, which replaces the merged row in the document.
  • The program counts the rows itself; with two line items in the data, the second row is gray.

Highlight Cells Depending on Their Values

The FieldMerged event tells you in which table cell a merged field is. With TableCell and the data of the record, your code formats a cell by its value. The following handler highlights prices above 150:

mailMerge.FieldMerged += (sender, e) =>
{
    if (e.TableCell != null &&
        e.MailMergeFieldAdapter is MergeField mergeField &&
        mergeField.Name == "Price" &&
        Convert.ToDouble(e.DataRow["Price"], System.Globalization.CultureInfo.InvariantCulture) > 150)
    {
        TableCellFormat cellFormat = e.TableCell.CellFormat;
        cellFormat.BackColor = System.Drawing.Color.LightYellow;
        e.TableCell.CellFormat = cellFormat;
    }
};

TableCell is null for a field outside a table, and DataRow returns the value of a column of the current record. The handler needs using TXTextControl.DocumentServer.Fields; for MergeField. The blog article Manipulating Table Cells During the MailMerge Process in .NET C# formats cells with rules like this.

Color the Text of a Merged Field

To format the merged text of a field rather than its cell, replace the field's content in FieldMerged. MergedField contains the merged field with its formatting in the TX Text Control format. The following handler shows the amount due in red:

mailMerge.FieldMerged += (sender, e) =>
{
    if (e.Merged && e.MailMergeFieldAdapter is MergeField mergeField && mergeField.Name == "AmountDue")
    {
        using (ServerTextControl fieldText = new ServerTextControl())
        {
            fieldText.Create();
            fieldText.Load(e.MergedField, BinaryStreamType.InternalUnicodeFormat);

            // Keep the text, remove the field, and color the text
            fieldText.ApplicationFields.Clear(true);
            fieldText.SelectAll();
            fieldText.Selection.ForeColor = System.Drawing.Color.Red;

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

The font and size of the field in the template stay, because the merged field is loaded with its formatting. For negative numbers, a number format with sections, such as #,##0.00;(#,##0.00), shows them in brackets without any code, as Merge Fields describes.

Number Formats and Formulas in Merged Tables

Table cells have number formats of their own. TableCellFormat.NumberFormat set on a cell in the template formats the numbers in that cell, including the values of merge fields in it.

Cells can also calculate values with formulas. In an invoice template, the line items come from the data, while formula cells calculate the line totals, the subtotal and the total from the merged values. The online demo Mail Merge and Formulas merges such a template, and Tables in Document Processing describes formulas and number formats.

Page Format of the Merged Document

MailMerge.UseTemplateFormat decides whether the page size and margins of the template are used. The default is false, in which case the PageSize, PageMargins and Landscape properties of the ServerTextControl in TextComponent are used.

Next Steps

  • Merge Fields: number and date formats and formatted text.
  • Template Design: design templates that look right with any data.
  • Tables in Document Processing: format tables and cells in code.