# Template Design

> Plan WPF mail merge templates: sample data, inspecting the DataSourceManager of the Reporting tab, TX versus DOCX storage, layout rules and a template test.

- **Product:** TX Text Control .NET for WPF
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for WPF > Mail Merge and Reporting > Template Design
- **5464 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-design/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-design/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-design/llms-full.txt

---

A template that looks perfect with three sample records can still fall apart with three hundred real ones. Good template design keeps merged documents clean whatever the data holds, and it starts with a few decisions that designers and developers of a WPF application make together: where templates are designed, with which data, in which format they are stored, which layout rules they follow and how they are tested.

Each element of a template is described on its own page: [Merge Fields](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/fields/llms-full.txt), [Merge Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/blocks/llms-full.txt), [Conditional Content](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/conditional-content/llms-full.txt), [Subtemplates](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/subtemplates/llms-full.txt) and [Images and Barcodes](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/images-barcodes/llms-full.txt). The examples work with the invoice template of [Create a Template](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/getting-started/mail-merge/template/llms-full.txt) in the `TextControl` named `textControl1` of *MainWindow.xaml*.

### Where Templates Are Designed

| Tool | Who uses it | What to know |
|---|---|---|
| Your application | Your users | A `TextControl` connected to a **Reporting** ribbon tab is a template designer. The tab lists the fields and blocks of the data you load into it |
| TX Text Control Words | You, while you develop | The TX Words *Reporting* sample for WPF is a complete template designer with sample templates and a sample database |
| MS Word | Users who work with Word documents | Merge fields are `MERGEFIELD` fields. DOCX templates are loaded with `LoadSettings` that import fields and merge blocks |
| Code | Developers | `MergeField` inserts merge fields, and `DataSourceManager.InsertMergeBlock` inserts complete merge blocks |

Whatever the tool, a template contains the names of fields and blocks and no connection to data. Any data of the same structure fills it.

The TX Words *Reporting* sample shows what a WPF template designer can look like: the **Reporting** tab, the **Field Navigation** sidebar with the blocks and fields of the invoice template, and a **Merge Settings** dialog box of the sample for a trial merge:

![TX Text Control Words for WPF with an invoice template, the Reporting tab, the Merge Settings dialog box and the Field Navigation sidebar listing the Sales_SalesOrderDetail block](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-wpf/reporting-field-navigation.de82662a.webp)

Its source code is in *%USERPROFILE%\\Documents\\TX Text Control 34.0.NET for WPF\\CSharp\\TX Words\\Reporting*. [Mail Merge Examples](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/examples/mail-merge/llms-full.txt) lists it with the other samples.

### Design With Sample Data

Designers work with sample data, which has the structure and the names of the real data but made-up values. Loaded into the `DataSourceManager` of the **Reporting** tab, it fills the drop-down menus: the columns of the master table become merge fields, related tables become merge blocks, and fields of a related table carry the table name as a prefix. At merge time, the real data takes its place.

[DataSourceManager](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/llms-full.txt) has a load method for each kind of data, listed in [Data Sources](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/#how-data-reaches-a-template). An instance of the class that your application merges later, filled with sample values, is a good choice, because the names in the tab then match the merge exactly. Data source configuration files don't work in WPF; see [Data Source Configuration Files](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/configuration-files/llms-full.txt).

A merge block can use fields of related tables. `Production_Product.Name` in the following table row block takes its value from the product that belongs to the order line:

![A table row merge block with the fields ProductNumber, Name, OrderQty, UnitPrice and LineTotal and an image placeholder, highlighted in red](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-wpf/merge-block-row-template.df401d03.webp)

### Inspect the Loaded Data Source

The `DataSourceManager` also tells your code what the loaded data contains, so that an application can show its own list of fields next to the **Reporting** tab or check a template against the data. Its properties describe the tables, columns and relations, and an event reports each change, for example when the designer loads other data or chooses another master table in **Select Master Table**.

Add a `ListBox` named `dataSourceList` to *MainWindow.xaml* and `Loaded="Window_Loaded"` to the `Window`. The handlers in *MainWindow.xaml.cs*, which needs `using System.IO;`, `using System.Windows;` and `using TXTextControl.DocumentServer.DataSources;`, load *invoice.json* into the tab and list what the tab offers:

```csharp
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    DataSourceManager dataSourceManager = ribbonReportingTab1.DataSourceManager;

    // Update the list when other data or another master table is chosen
    dataSourceManager.PossibleMergeFieldColumnsChanged += (s, args) => ShowDataSource();
    dataSourceManager.PossibleMergeBlockTablesChanged += (s, args) => ShowDataSource();

    dataSourceManager.LoadJson(File.ReadAllText("invoice.json"));
}

private void ShowDataSource()
{
    DataSourceManager dataSourceManager = ribbonReportingTab1.DataSourceManager;
    DataTableInfo master = dataSourceManager.MasterDataTableInfo;

    dataSourceList.Items.Clear();

    if (master == null)
    {
        return;
    }

    dataSourceList.Items.Add($"Master table: {master.TableName}");

    // The columns that Insert Merge Field offers
    foreach (DataColumnInfo column in dataSourceManager.PossibleMergeFieldColumns)
    {
        dataSourceList.Items.Add($"Field {column.ColumnName} ({column.DataType.Name}) in {column.DataTableInfo.TableName}");
    }

    // The tables that Insert Merge Block offers
    foreach (DataTableInfo table in dataSourceManager.PossibleMergeBlockTables)
    {
        dataSourceList.Items.Add($"Block {table.TableName} with {table.Columns.Count} columns");
    }

    foreach (DataRelationInfo relation in dataSourceManager.DataRelations)
    {
        dataSourceList.Items.Add($"Relation {relation.ParentTableName}.{relation.ParentColumnName} to {relation.ChildTableName}.{relation.ChildColumnName}");
    }
}
```

The code works as follows:

- [MasterDataTableInfo](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/masterdatatableinfo-property/llms-full.txt) is the master table, a [DataTableInfo](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datatableinfo/llms-full.txt). Setting it chooses another master table, as **Select Master Table** does, and changes the two lists that follow.
- [PossibleMergeFieldColumns](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/possiblemergefieldcolumns-property/llms-full.txt) returns the columns that can be merge fields for the master table, each a [DataColumnInfo](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datacolumninfo/llms-full.txt) with its [ColumnName](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datacolumninfo/columnname-property/llms-full.txt), its [DataType](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datacolumninfo/datatype-property/llms-full.txt) and the [DataTableInfo](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datacolumninfo/datatableinfo-property/llms-full.txt) it belongs to. [Caption](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datacolumninfo/caption-property/llms-full.txt) is a friendly name for the column, the column name unless the data source sets one.
- [PossibleMergeBlockTables](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/possiblemergeblocktables-property/llms-full.txt) returns the tables that can be merge blocks for the master table. A table's [Columns](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datatableinfo/columns-property/llms-full.txt), [ChildTables](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datatableinfo/childtables-property/llms-full.txt) and [ChildRelations](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datatableinfo/childrelations-property/llms-full.txt) describe it further; `ChildRelations` is empty when the data comes from objects, and `ChildTables` then follows the properties of the classes.
- [DataRelations](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/datarelations-property/llms-full.txt) lists the relations between the tables, each a [DataRelationInfo](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datarelationinfo/llms-full.txt) with the parent and child table and column names and, if one was defined, a [RelationName](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datarelationinfo/relationname-property/llms-full.txt). [DataTables](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/datatables-property/llms-full.txt) lists all tables, master table or not.
- The collections are read-only: [DataTableInfoCollection](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datatableinfocollection/llms-full.txt) and the collections of columns and relations throw a `NotSupportedException` on `Add`, `Clear` or `Remove`. A table is found by its name, as in `dataSourceManager.DataTables["Items"]`.

Each property has an event that occurs when its value changes:

| Event | Occurs when this changes |
|---|---|
| [DataTablesChanged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/datatableschanged-event/llms-full.txt), [DataRelationsChanged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/datarelationschanged-event/llms-full.txt) | The tables and the relations of the data source |
| [MasterDataTableInfoChanged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/masterdatatableinfochanged-event/llms-full.txt) | The master table |
| [PossibleMergeFieldColumnsChanged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/possiblemergefieldcolumnschanged-event/llms-full.txt), [PossibleMergeBlockTablesChanged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/possiblemergeblocktableschanged-event/llms-full.txt) | The columns and tables available for the current master table |
| [IsMergingPossibleChanged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/ismergingpossiblechanged-event/llms-full.txt) | [IsMergingPossible](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/ismergingpossible-property/llms-full.txt), which is `true` when [Merge](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/merge-method/llms-full.txt) can be called, for example to enable a preview button |
| [ConnectionStringChanged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/connectionstringchanged-event/llms-full.txt), [DataProviderNameChanged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/dataprovidernamechanged-event/llms-full.txt) | [ConnectionString](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/connectionstring-property/llms-full.txt) and [DataProviderName](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/dataprovidername-property/llms-full.txt) of a database connection selected in the database connection dialog box; both are empty strings when no connection was selected |

Code that inserts merge blocks itself takes the table and column names from this information: [Merge Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/blocks/llms-full.txt) inserts a block with `InsertMergeBlock`, and [MergeBlockSettings.TableID](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/tableid-property/llms-full.txt) gives the table it creates an ID, 0 for none or a value from 10 to `short.MaxValue`.

### Choose the Storage Format

A template is stored in the TX Text Control format or as a DOCX file:

|  | TX Text Control format | DOCX |
|---|---|---|
| Save | `StreamType.InternalUnicodeFormat` | `StreamType.WordprocessingML` |
| Merge fields and blocks | Kept as fields and subtext parts | Saved as MS Word fields and bookmarks |
| Load for a merge | Without load settings | With `LoadSettings`: `ApplicationFieldFormat` set to `MSWord` and `LoadSubTextParts` set to `true` |
| Edit in MS Word | No | Yes |

Templates that users design in your application are best stored in the TX Text Control format; DOCX suits templates that must stay editable in MS Word. Two buttons in *MainWindow.xaml*, with `Click="saveTemplateButton_Click"` and `Click="openTemplateButton_Click"`, save and open the template:

```csharp
private void saveTemplateButton_Click(object sender, RoutedEventArgs e)
{
    // The TX Text Control format keeps fields, merge blocks and their rules
    textControl1.Save("invoice.tx", StreamType.InternalUnicodeFormat);
}

private void openTemplateButton_Click(object sender, RoutedEventArgs e)
{
    textControl1.Load("invoice.tx", StreamType.InternalUnicodeFormat);
}
```

[LoadSubTextParts](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/loadsettings/loadsubtextparts-property/llms-full.txt) is used for DOCX, DOC and RTF only, where a merge block is stored as a bookmark and turned back into a subtext part on loading. [Sorting and Filtering](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/sorting-filtering/llms-full.txt) describes the rules a block can carry.

### Follow Layout Rules That Merge Well

Merged content changes the length of paragraphs, tables and pages. These rules keep a template stable:

- **Page break before, not page breaks.** A forced page break can produce an empty page when the text before it grows. Set "page break before" on the paragraph or its style, as the blog article [Document Templates Tip: Say No to Forced Page Breaks](https://www.textcontrol.com/blog/2024/05/28/document-templates-tip-say-no-to-forced-page-breaks/) recommends.
- **Tables that break well.** Repeat header rows on each page and keep related rows together; the blog article [Why Table Control in Templates is Important for Professional PDF Creation in C#](https://www.textcontrol.com/blog/2025/04/04/why-table-control-in-templates-is-important-for-professional-pdf-creation/) explains the settings.
- **Headers inside the block.** A header row outside a merge block appears once. Make the whole table the block when each record needs its own header, as [Merge Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/blocks/#what-a-merge-block-contains) describes.
- **No empty lines.** `RemoveEmptyLines` of `MailMerge` removes a line that is empty after merging, but only if nothing else is on that line, so give optional fields a line of their own.
- **Optional content in IF fields and blocks.** Text that depends on the data belongs in an `IF` field or a merge block; see [Conditional Content](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/conditional-content/llms-full.txt).
- **Shared content in subtemplates.** Text that many templates share, such as terms and conditions, goes into a subtemplate that an `INCLUDETEXT` field includes; see [Subtemplates](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/subtemplates/llms-full.txt).

The blog article [Designing the Perfect Contract Template for MailMerge in C#](https://www.textcontrol.com/blog/2024/09/26/designing-the-perfect-contract-template-for-mailmerge-in-csharp/) applies these rules to a contract.

### Check What a Template Contains

Three functions show designers the structure of their template:

- **Highlight Merge Fields** and **Highlight Merge Blocks**, the last entries of the **Insert Merge Field** and **Insert Merge Block** drop-down menus, mark fields and blocks in the text.
- **Field Navigation** in the **View** group shows a sidebar with all merge blocks and merge fields. It needs a `Sidebar` in the window, as [Reporting Ribbon and WPF Dialog Boxes](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/ribbon-dialogs/#show-the-field-navigation-sidebar) shows.
- A preview merges the template with the sample data in the editor. [Template Preview](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-preview/llms-full.txt) builds one.

### Test a Template

Merge a template with sample data before it is used, and look for fields that stayed empty. The following handler of a **Test Template** button, `Click="testTemplateButton_Click"` in XAML, merges the template of the editor with *invoice.json*, reports the merge blocks and every merge field without data and saves a PDF to look at:

```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using TXTextControl;
using TXTextControl.DocumentServer;
using TXTextControl.DocumentServer.Fields;
```

```csharp
private void testTemplateButton_Click(object sender, RoutedEventArgs e)
{
    List<string> report = new List<string>();

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

        // Test the template as it is in the editor
        byte[] template;
        textControl1.Save(out template, BinaryStreamType.InternalUnicodeFormat);
        tx.Load(template, BinaryStreamType.InternalUnicodeFormat);

        using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
        {
            report.Add("Merge blocks: " + string.Join(", ", mailMerge.GetBlockNames()));

            // Note every merge field that the sample data doesn't fill
            mailMerge.FieldMerged += (s, args) =>
            {
                if (!args.Merged && args.MailMergeFieldAdapter is MergeField mergeField)
                {
                    string block = args.MergeBlockName == "" ? "" : $" in block {args.MergeBlockName}";
                    report.Add($"No data for {mergeField.Name}{block}");
                }
            };

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

        tx.Save("invoice-test.pdf", StreamType.AdobePDF);
    }

    MessageBox.Show(this, string.Join(Environment.NewLine, report), "Template Test");
}
```

The code works as follows:

- The template leaves the editor as a byte array in the TX Text Control format and is merged in a `ServerTextControl`; the editor keeps the unmerged template.
- [GetBlockNames](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/getblocknames-method/llms-full.txt) lists the merge blocks of the template.
- [FieldMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/fieldmerged-event/llms-full.txt) occurs for every field; [Merged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/merged-property/llms-full.txt) is `false` for a field without data, and [MergeBlockName](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/mergeblockname-property/llms-full.txt) names its block.

For the invoice and its data, the message names the merge block of the items and no field without data. A typo in a field name, such as `Compny`, would show up in the list.

Fonts and linked images can differ between computers as well. The blog article [Check TX Text Control Template Dependencies Before Deployment](https://www.textcontrol.com/blog/2026/07/28/check-tx-text-control-template-dependencies-before-deployment/) checks them together with the field names before deployment.

### Next Steps

- [Reporting Ribbon and WPF Dialog Boxes](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/ribbon-dialogs/llms-full.txt): the buttons and dialog boxes of the Reporting tab.
- [Template Preview](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-preview/llms-full.txt): the merged result while designing.
- [Formatting and Styling](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/styling/llms-full.txt): formatting that depends on the data.
- [Data Sources](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/llms-full.txt): the data that fills the template.

---

## Nearby

- **Previous:** [Formatting and Styling](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/styling/llms-full.txt)
- **Next:** [Reporting Ribbon and WPF Dialog Boxes](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/ribbon-dialogs/llms-full.txt)
