TX Text Control .NET for WPF

Add Repeating Merge Blocks

Topic

How many line items will an invoice have? The template can't know, so it contains the row for one item and lets the merge repeat it. Such a repeated part is a merge block. Continuing the invoice of the previous two tutorials, you add a block for the Items of invoice.json in the WPF window, with the Reporting tab or in code, merge it, and then fill the same template from business objects instead of JSON.

Prerequisites

How Merge Blocks Work

A merge block is a named range of the template, a subtext part, whose name is txmb_ followed by the name of a list in the data. The invoice needs the block txmb_Items for the Items array of the JSON data. During the merge, the range is copied once for each item, together with its formatting, and the merge fields inside it take the values of that item: Description, Quantity and Price.

Any content can form a block, from a table row to several paragraphs or a whole table. For line items, a table row is the usual choice, because the table grows by one row per item. In the following template of the WPF mail merge sample, the highlighted table row is a merge block:

A template in the WPF mail merge sample with merge fields above a table whose highlighted second row is a merge block.

After the merge, the table has one row for each record of the block's data:

The merged document in the WPF mail merge sample, in which the table has two rows created from the merge block.

Insert the Block With the Ribbon

  1. Press F5 and open or design the template as in Create a Template.

  2. Place the input position where the table of items belongs.

  3. Open the Insert Merge Block drop-down in the Merge Blocks group of the Reporting tab and select Items.

    The Insert Merge Block drop-down of the WPF Reporting tab, listing the tables of a data source.

  4. In the dialog box, select the columns Description, Quantity and Price and click OK.

  5. Save the template again as invoice.docx.

The tab inserts a table row with a merge field for each selected column and marks it as the block. Highlight Merge Blocks at the bottom of the same drop-down marks the blocks of the template, and the field navigator of Create a Template lists them on its Merge Blocks tab.

Insert the Block in Code

Your code can insert the same block, with or without asking the user. Add these using directives to MainWindow.xaml.cs:

using System.Collections.Generic;
using TXTextControl.DocumentServer.DataSources;
using TXTextControl.DocumentServer.WPF;

Then add two buttons, insertItemsBlockButton and insertItemsBlockDialogButton, to the DockPanel of MainWindow.xaml and these handlers to MainWindow:

private void insertItemsBlockButton_Click(object sender, RoutedEventArgs e)
{
    // The Items block and the merge fields it contains
    MergeBlockInfo mergeBlockInfo = new MergeBlockInfo("Items")
    {
        ColumnNames = new List<string> { "Description", "Quantity", "Price" }
    };

    // A table row that is repeated for each item, below a header row
    MergeBlockSettings mergeBlockSettings = new MergeBlockSettings(BlockTemplateType.TableRow, true);

    // Insert the block at the current input position
    ribbonReportingTab1.DataSourceManager.InsertMergeBlock(textControl1, mergeBlockInfo, mergeBlockSettings);
}

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

    // Let the user choose the columns of the Items block
    InsertMergeBlockDialog dialog = new InsertMergeBlockDialog(
        dataSourceManager, textControl1, dataSourceManager.DataTables["Items"])
    {
        Owner = this
    };

    dialog.ShowDialog();
}

The code works as follows:

  • MergeBlockInfo names the list in the data, and its ColumnNames become the merge fields of the block.
  • MergeBlockSettings with BlockTemplateType.TableRow creates the block as a table row, and true adds a separate header row above it.
  • InsertMergeBlock inserts the block at the input position of the WPF TextControl that you pass.
  • InsertMergeBlockDialog of the namespace TXTextControl.DocumentServer.WPF is the dialog box of the Reporting tab. It is a WPF Window: set its Owner and open it with ShowDialog. DataTables returns the table of the loaded data by its name.

Merge the Template

The merge code of Merge Data into Fields needs no change. LoadSubTextParts set to true imports the block with the template, and MergeJsonData fills it from the Items array the way it fills the fields from the other properties.

Click Merge. The invoice now contains a table with one row for Consulting and one for Training. A block without data is removed with its content, because RemoveEmptyBlocks is true by default.

Merge Business Objects Instead

The same template also works with business objects. A property that holds a collection fills the block of the same name. Add these classes to the project:

using System.Collections.Generic;

public class Invoice
{
    public string Company { get; set; }
    public string Name { get; set; }
    public string InvoiceNumber { get; set; }
    public List<InvoiceItem> Items { get; set; } = new List<InvoiceItem>();
}

public class InvoiceItem
{
    public string Description { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

In mergeButton_Click, replace the call of MergeJsonData with a call of MergeObject:

Invoice invoice = new Invoice { Company = "Contoso Ltd.", Name = "Jane Miller", InvoiceNumber = "INV-1001" };
invoice.Items.Add(new InvoiceItem { Description = "Consulting", Quantity = 2, Price = 150.00m });
invoice.Items.Add(new InvoiceItem { Description = "Training", Quantity = 1, Price = 480.00m });

mailMerge.MergeObject(invoice);

The merged invoice is the same as with the JSON data.

Next Steps