Add Repeating Merge Blocks
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
- The ribbon window from Create a Template, which loads invoice.json into the Reporting tab when it opens.
- The Merge button and its handler from Merge Data into Fields.
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:

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

Insert the Block With the Ribbon
Press F5 and open or design the template as in Create a Template.
Place the input position where the table of items belongs.
Open the Insert Merge Block drop-down in the Merge Blocks group of the Reporting tab and select Items.

In the dialog box, select the columns Description, Quantity and Price and click OK.
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.TableRowcreates the block as a table row, andtrueadds a separate header row above it. - InsertMergeBlock inserts the block at the input position of the WPF
TextControlthat you pass. - InsertMergeBlockDialog of the namespace
TXTextControl.DocumentServer.WPFis the dialog box of the Reporting tab. It is a WPFWindow: set itsOwnerand open it withShowDialog. 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
- Merge Blocks: block types, empty blocks and block events in detail.
- Nested Blocks and Master-Detail Reports: blocks inside blocks.
- Sorting and Filtering: sort and filter the rows of a block.
- The blog article Mail Merge: Inserting Merge Blocks using the DataSourceManager in C#: more ways to insert blocks in code.