Conditional Content
Should an invoice remind the customer to pay, or thank them for paying? Should a contract contain the non-compete clause? One template can answer such questions from the data. With TX Text Control, most rules for conditional content are part of the template, where users of the Reporting tab see and maintain them, and the merge code of your WPF application stays the same: IF fields insert one of two texts, merge blocks include or remove sections, conditions and filters act on records, and MailMerge events cover what templates can't express.
All examples merge the invoice template invoice.docx of Merge Data into Fields with invoice.json. Leaving out single rows of a block is the subject of Sorting and Filtering.
Choose a Technique
| To | Use | Where the rule lives |
|---|---|---|
| Insert one of two texts depending on a value | An IF field | Template |
| Include a section only when its data exists | A merge block bound to an object or an array | Template |
| Merge a nested block only when its parent record meets a condition | The block's merging condition | Template |
| Leave out single rows of a block | Filter instructions of the block | Template |
| Include or leave out a subtemplate | An INCLUDETEXT field and the IncludeTextMerging event | Template and code |
| Change a merged value with your own logic | The FieldMerged event | Code |
Insert IF Fields
An IF field compares the value of a merge field with another value and inserts the text for true or the text for false. Its MS Word field code reads IF Expression1 Operator Expression2 TrueText FalseText; Expression1 is the name of a merge field, and the operator is equal to, not equal to, greater than, less than, greater than or equal to or less than or equal to.
For the examples, invoice.json gets an AmountDue and a DueDate:
[
{
"Company": "Contoso Ltd.",
"Name": "Jane Miller",
"InvoiceNumber": "INV-1001",
"AmountDue": 780.00,
"DueDate": "2026-10-31",
"Items": [
{ "Description": "Consulting", "Quantity": 2, "Price": 150.00 },
{ "Description": "Training", "Quantity": 1, "Price": 480.00 }
]
}
]
Users of the WPF Reporting tab insert the field with IF in the Insert Special Field drop-down of the Merge Fields group and fill in the field name, the comparison and the two texts. Your code creates it with IfField. Add a button with Click="insertIfFieldButton_Click" to MainWindow.xaml, using TXTextControl.DocumentServer.Fields; to MainWindow.xaml.cs and this handler:
private void insertIfFieldButton_Click(object sender, RoutedEventArgs e)
{
IfField ifField = new IfField
{
Expression1 = "AmountDue", // the name of a merge field
Operator = IfField.RelationalOperator.GreaterThan,
Expression2 = "0",
TrueText = "Please pay the amount due by the due date.",
FalseText = "Thank you, this invoice has been paid."
};
textControl1.ApplicationFields.Add(ifField.ApplicationField);
}
The code works as follows:
- Expression1 is the merge field that is compared, and Expression2 the value it is compared with.
- Operator takes
Equals,NotEqual,LessThan,GreaterThan,GreaterThanOrEqualToorLessThanOrEqualTo. - TrueText and FalseText are the two texts.
- DataSourceCulture is the culture for comparing numbers; by default, the culture of the current thread.
Since AmountDue is 780.00, the merged invoice asks for payment, in the formatting of the field. Inside a merge block, the field is evaluated per record. Load templates with ApplicationFieldFormat.MSWord so that IF fields from DOCX files are imported as fields.
Include Optional Sections
A block bound to a single nested object is merged once when the object exists. When the data lacks the object, or a block's array is empty or missing, the block disappears with its content, because RemoveEmptyBlocks is true by default. Such a block can hold complete clauses, tables or images.
In a contract template, each optional clause is a block named like an object of the data:
[
{
"company": "Acme Corporation",
"section_term": { "months": 24 },
"section_compete": { "months": 12 }
}
]
Without section_compete in a contract's data, the non-compete clause is left out and the text after it moves up. The blog article Generating Dynamic NDAs Using TX Text Control MailMerge in C# .NET builds an agreement from such sections, and Merge Blocks shows how blocks are inserted.
A header row outside a removed block stays behind; to remove a whole table, put its header row into the block.
Merge a Block Only for Matching Parent Records
A nested block can depend on its parent record, for example articles listed only for orders above an amount. The condition is the block's BlockMergingCondition, stored in the template, so the merge code doesn't change. Sorting and Filtering sets one; the blog article MailMerge: Rendering Conditional Table Rows adds a note row only for products that aren't available.
Include Subtemplates Depending on Your Code
The IncludeTextMerging event occurs for each INCLUDETEXT field, and an empty document assigned in its handler leaves the subtemplate out. Here, a field of the window decides whether terms.docx is included; MainWindow.xaml.cs needs using TXTextControl.DocumentServer; for the event arguments:
private bool includeTerms = false; // decided by your application
private void MailMerge_IncludeTextMerging(object sender, MailMerge.IncludeTextMergingEventArgs e)
{
if (includeTerms || !e.Filename.EndsWith("terms.docx"))
{
return;
}
// An empty document leaves the subtemplate out
using (ServerTextControl empty = new ServerTextControl())
{
empty.Create();
byte[] data;
empty.Save(out data, BinaryStreamType.InternalUnicodeFormat);
e.IncludeTextDocument = data;
}
}
Attach the handler with mailMerge.IncludeTextMerging += MailMerge_IncludeTextMerging; before calling the merge method. Subtemplates covers INCLUDETEXT fields and the search path. For a decision per record, the blog article MailMerge: Conditional INCLUDETEXT Fields tracks the current record in the DataRowMerged event and reads a flag from the data.
Change a Merged Value in Code
When the rule is too complex for a template, handle FieldMerged. DataRow gives the whole record, so the text of one field can depend on another. With using System; and using System.Globalization;, this handler shows "Paid" instead of the due date when nothing is due:
mailMerge.FieldMerged += (s, args) =>
{
// Show "Paid" instead of the due date when nothing is due
if (args.MailMergeFieldAdapter is MergeField mergeField &&
mergeField.Name == "DueDate" &&
args.DataRow != null &&
Convert.ToDecimal(args.DataRow["AmountDue"], CultureInfo.InvariantCulture) == 0)
{
mergeField.ApplicationField.Text = "Paid";
}
};
MailMergeFieldAdapter is the name of the property in the compiled assembly; the reference describes the event arguments on the page of FieldMergedEventArgs. Merge Events covers the event in full.
Remove What Stays Empty
Five properties of MailMerge remove what optional content leaves behind:
| Property | Removes | Default |
|---|---|---|
| RemoveEmptyFields | Merge fields without data | true |
| RemoveEmptyBlocks | Merge blocks without data, with their content | true |
| RemoveEmptyLines | Lines that are empty after the merge | false |
| RemoveEmptyImages | Image placeholders without data | false |
| RemoveTrailingWhitespace | Trailing whitespace, before the document is saved | true |
Conditional Instructions Are for Forms
The conditional instructions of TX Text Control change form fields while a user fills in a form, for example by disabling a drop-down list while another field has no value. They work in the editor, not in the merge; Form Fields and Conditional Instructions describes them for WPF.
Next Steps
- Sorting and Filtering: filter the rows of a block and set merging conditions.
- Subtemplates: reusable sections included with
INCLUDETEXTfields. - Merge Fields: formats and text for fields without data.
- Template Preview: check the variants of a template while designing.