Formatting and Styling
Where does the look of a merged document come from? Mostly from the template: fonts, colors, table borders and paragraph settings are designed in the TextControl of your WPF window and survive the merge unchanged. Formatting that depends on the data, such as shaded alternate rows or a highlighted value, is added either by fields in the template or by handlers of the MailMerge events in MainWindow.xaml.cs.
Number and date formats of single merge fields belong to Merge Fields. The examples here continue the invoice of the Getting Started tutorials, the template invoice.docx with the table row block Items from Add Repeating Merge Blocks and the data invoice.json with two items: Consulting (quantity 2, price 150.00) and Training (quantity 1, price 480.00).
What the Merge Keeps
The merge fills the template with values and leaves its design alone:
- Merge fields keep their formatting. A value takes the font, size, style and color that its field has in the template.
- Merge blocks are copied with their formatting. Every record gets a copy of the block with its table formatting, paragraph settings and styles.
- Letter case is an option of the field: the Format list of the Merge Field Options dialog box offers UPPERCASE, lowercase, First capital and Title Case.
In the mail merge sample of the WPF installation, the template's table row block has bold names and italic quantities:

After the merge, each record has a row of its own, and every row keeps the bold and italic formatting of the template:

Keep formatting out of the data and in the template. Template Design lists the layout rules for templates that grow with their data.
Alternate Row Colors in the Template
A template can alternate row colors on its own. Build the merge block from 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 NEXT field advances to the next record, so the second row shows the following record in the second color. In the Reporting tab, the field is inserted with Insert Special Field in the Merge Fields group.
With an odd number of records, the second row of the last copy is left empty. The blog article Reporting: Removing Empty Table Rows removes that row in the BlockRowMerged event.
Alternate Row Colors in Code
For a block of one row, your code colors every second row. The BlockRowMerged event occurs after each row of a block and passes the merged row as a document. Add a button to MainWindow.xaml, next to the Merge button of the Getting Started tutorials:
<Button x:Name="colorRowsButton" DockPanel.Dock="Top" Content="Merge With Row Colors" Click="colorRowsButton_Click" />
The handlers go into MainWindow.xaml.cs. MailMerge events are ordinary .NET events, so your code attaches them with +=:
using System;
using System.IO;
using System.Windows;
using TXTextControl;
using TXTextControl.DocumentServer;
using TXTextControl.DocumentServer.Fields;
private int itemRowCount;
private void colorRowsButton_Click(object sender, RoutedEventArgs e)
{
byte[] result;
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 })
{
mailMerge.BlockMerging += MailMerge_BlockMerging;
mailMerge.BlockRowMerged += MailMerge_BlockRowMerged;
mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
}
tx.Save(out result, BinaryStreamType.InternalUnicodeFormat);
}
// Show the merged invoice in the editor
textControl1.Load(result, BinaryStreamType.InternalUnicodeFormat);
}
private void MailMerge_BlockMerging(object sender, MailMerge.BlockMergingEventArgs e)
{
// Start counting again for each Items block
if (e.BlockName == "Items")
{
itemRowCount = 0;
}
}
private void MailMerge_BlockRowMerged(object sender, MailMerge.BlockRowMergedEventArgs e)
{
if (e.MergeBlockName != "Items")
{
return;
}
// Color every second row of the block
itemRowCount++;
if (itemRowCount % 2 != 0)
{
return;
}
using (ServerTextControl rowText = new ServerTextControl())
{
rowText.Create();
rowText.Load(e.MergedBlockRow, BinaryStreamType.InternalUnicodeFormat);
foreach (TXTextControl.Table table in rowText.Tables)
{
foreach (TXTextControl.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;
}
}
The code works as follows:
- The merge runs in a
ServerTextControl, and the result is loaded intotextControl1, the editor of the window. - BlockMerging occurs before a block is merged, and its BlockName resets the counter for each
Itemsblock, so that every invoice of a larger array starts with a white row. - MergeBlockName limits the change to the
Itemsblock. - MergedBlockRow contains the row in the internal TX Text Control format. A second
ServerTextControlloads it, TableRow.CellFormat changes the background of its cells, and the saved bytes replace the row. - The colors of
TableCellFormatareSystem.Drawing.Colorvalues, not WPF colors, so the code names the namespace in full.
Click Merge With Row Colors: the second row, Training, is gray.
Highlight Cells Depending on Their Values
FieldMerged occurs for every merged field and passes the table cell the field is in. A handler can color that cell depending on the data of the record. Attach it with mailMerge.FieldMerged += MailMerge_FieldMerged;:
private void MailMerge_FieldMerged(object sender, MailMerge.FieldMergedEventArgs e)
{
// Highlight quantities above 1
if (e.TableCell != null &&
e.MailMergeFieldAdapter is MergeField mergeField &&
mergeField.Name == "Quantity" &&
Convert.ToInt32(e.DataRow["Quantity"]) > 1)
{
TableCellFormat cellFormat = e.TableCell.CellFormat;
cellFormat.BackColor = System.Drawing.Color.LightYellow;
e.TableCell.CellFormat = cellFormat;
}
}
- TableCell is the cell of the field, or
nulloutside tables. - MailMergeFieldAdapter is the merged field. A merge field is a MergeField, whose name the rule checks.
- DataRow gives the values of the current record by column name.
The quantity cell of Consulting turns yellow. The blog article Manipulating Table Cells During the MailMerge Process in .NET C# shows further rules of this kind.
Color the Text of a Merged Field
To change the merged text itself, replace the content of the field. MergedField contains the merged field with its formatting, and the handler assigns a changed version back. This handler turns prices above 400 red; attach it with mailMerge.FieldMerged += ColorPrice_FieldMerged;:
private void ColorPrice_FieldMerged(object sender, MailMerge.FieldMergedEventArgs e)
{
if (e.Merged &&
e.MailMergeFieldAdapter is MergeField mergeField &&
mergeField.Name == "Price" &&
Convert.ToDecimal(e.DataRow["Price"], System.Globalization.CultureInfo.InvariantCulture) > 400)
{
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;
}
}
}
Merged is true when the field received a value. ApplicationFields.Clear with true removes the field and keeps its text, which is then colored. Font and size stay as designed, because the field is loaded with its formatting.
Number Formats and Formulas in Merged Tables
A table cell can have a number format of its own. With a number as the cell's text type, TableCellFormat.NumberFormat formats the numbers in the cell, and that includes the value of a merge field placed in it, according to the blog article Formatting Numbers in Table Cells.
Formula cells complete an invoice: the items come from the data, and formulas calculate the line totals and the total from the merged numbers. See Tables and Calculations in Document Processing for formulas and number formats.
Next Steps
- Merge Fields: formats, text before and after the value and formatted text.
- Template Design: templates that work with small and large data.
- Merge Events: all events of
MailMergeand their arguments. - Tables and Calculations: format tables and cells in code.