TX Text Control .NET for WPF

Conditional Content

Topic

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

ToUseWhere the rule lives
Insert one of two texts depending on a valueAn IF fieldTemplate
Include a section only when its data existsA merge block bound to an object or an arrayTemplate
Merge a nested block only when its parent record meets a conditionThe block's merging conditionTemplate
Leave out single rows of a blockFilter instructions of the blockTemplate
Include or leave out a subtemplateAn INCLUDETEXT field and the IncludeTextMerging eventTemplate and code
Change a merged value with your own logicThe FieldMerged eventCode

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, GreaterThanOrEqualTo or LessThanOrEqualTo.
  • 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:

PropertyRemovesDefault
RemoveEmptyFieldsMerge fields without datatrue
RemoveEmptyBlocksMerge blocks without data, with their contenttrue
RemoveEmptyLinesLines that are empty after the mergefalse
RemoveEmptyImagesImage placeholders without datafalse
RemoveTrailingWhitespaceTrailing whitespace, before the document is savedtrue

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