# Merge Blocks

> Repeat content with merge blocks in WPF: InsertMergeBlockDialog results, paragraph blocks with MergeBlockSettings, GetBlockNames and the current block.

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

---

Whatever repeats in a merged document, such as the line items of an invoice or the clauses of a contract, comes from a merge block: a part of the template that `MailMerge` copies once for each record of a list. A block can also stand for a single object and disappear when the data lacks it. Building on the first block of Getting Started, you learn what a block may contain and how it is stored, which block tools the WPF Reporting tab offers, and how your code inserts, checks and inspects blocks in the WPF `TextControl`.

[Add Repeating Merge Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/getting-started/mail-merge/blocks/llms-full.txt) in Getting Started inserts the `Items` block of the invoice template *invoice.docx* and merges it with *invoice.json*. The code below extends the same window: `textControl1` in *MainWindow.xaml*, connected to the Reporting tab `ribbonReportingTab1`, which has *invoice.json* loaded. [Nested Blocks and Master-Detail Reports](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/nested-blocks/llms-full.txt) covers blocks inside blocks.

### What a Merge Block Contains

Besides a table row, a block can hold a complete table, paragraphs, images, text frames or a single word. `MailMerge` copies the whole block with its formatting for each record:

- **Breaks are part of the block.** A paragraph end in the block starts a new paragraph per record, and a page break, section break or column break in the block is repeated per record.
- **A new page per record.** When `PageBreakBefore` is set in the paragraph format of the block's first paragraph, each copy starts on a new page; the blog article [MailMerge: Starting Each Merge Block on a New Page](https://www.textcontrol.com/blog/2016/09/09/mailmerge-starting-each-merge-block-on-a-new-page/) shows the layout.
- **Table header rows.** A header row above a table row block is repeated at the top of each page. For a header per copy of the block, make the whole table the block; [Repeat Table Headers per Block](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/nested-blocks/#repeat-table-headers-per-block) shows why this matters for nested blocks.
- **Fields, images and barcodes** in the block receive the values of the current record.

### How Blocks Are Stored

A merge block is a [SubTextPart](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/subtextpart/llms-full.txt), a named range of text, whose name starts with `txmb_`, the value of [MailMerge.MergeBlockNamePrefix](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/mergeblocknameprefix-property/llms-full.txt), followed by the block name. [Subtext Parts](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-editor/subtext-parts/llms-full.txt) covers the element in the editor.

- **DOCX, DOC and RTF templates** are loaded with [LoadSettings.LoadSubTextParts](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/loadsettings/loadsubtextparts-property/llms-full.txt) set to `true`, as in [Merge Data into Fields](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/getting-started/mail-merge/fields/llms-full.txt); without it, their blocks aren't imported.
- **Older templates** mark the start and the end of a block with two document targets, `blockstart_` and `blockend_` followed by the block name. This format is still supported.
- **Saving.** [ReportingMergeBlockFormat](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/loadsavesettingsbase/reportingmergeblockformat-property/llms-full.txt) of `SaveSettings` keeps blocks as they are with `Default` or converts them into the older document targets with `DocumentTargets`.

A block also stores its sorting and filter instructions, as [Sorting and Filtering](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/sorting-filtering/#where-the-instructions-are-stored) describes.

### Work With Blocks in the Reporting Tab

The **Merge Blocks** group of the WPF Reporting tab inserts a block for a table of the data source through the **Insert Merge Block** dialog box, makes selected text a custom block and renames or removes blocks with **Edit Merge Block**. [Reporting Ribbon and WPF Dialog Boxes](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/ribbon-dialogs/#merge-blocks) describes these commands and the options of the dialog box.

### Check the Result of the Dialog Box

The dialog boxes are WPF windows of the namespace `TXTextControl.DocumentServer.WPF`: set their `Owner` and open them with `ShowDialog`, which returns `true` when the user clicks **OK**. After [InsertMergeBlockDialog](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-wpf/insertmergeblockdialog/llms-full.txt) has closed, its properties tell your code what the user inserted. Add a button with `Click="insertBlockWithDialogButton_Click"` to *MainWindow.xaml* and this handler to *MainWindow.xaml.cs*, with `using TXTextControl.DocumentServer.DataSources;` and `using TXTextControl.DocumentServer.WPF;`:

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

    InsertMergeBlockDialog dialog = new InsertMergeBlockDialog(
        dataSourceManager, textControl1, dataSourceManager.DataTables["Items"])
    {
        Owner = this
    };

    // ShowDialog returns true when the user clicks OK
    if (dialog.ShowDialog() != true)
    {
        return;
    }

    if (dialog.AddResult != SubTextPartCollection.AddResult.Successful)
    {
        MessageBox.Show(this, "The merge block was not inserted: " + dialog.AddResult);
        return;
    }

    // A table row block: format the table the dialog created
    if (dialog.BlockType == BlockTemplateType.TableRow && dialog.CreatedTable != null)
    {
        dialog.CreatedTable.Select();
        textControl1.Selection.FontSize = 200; // 10 points in twips
    }
}
```

The code works as follows:

- [AddResult](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-wpf/insertmergeblockdialog/addresult-property/llms-full.txt) is a value of [SubTextPartCollection.AddResult](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/subtextpartcollection/addresult-enumeration/llms-full.txt): `Successful`, or a reason such as `Overlapping` when the new block would overlap another one.
- [BlockType](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-wpf/insertmergeblockdialog/blocktype-property/llms-full.txt) is the kind of block the user chose, and [CreatedTable](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-wpf/insertmergeblockdialog/createdtable-property/llms-full.txt) the table of a table row block.
- [MergeBlockInfo](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-wpf/insertmergeblockdialog/mergeblockinfo-property/llms-full.txt) describes the inserted block, and [HighlightColor](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-wpf/insertmergeblockdialog/highlightcolor-property/llms-full.txt) the color the user selected.

[EditMergeBlocksDialog](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-wpf/editmergeblocksdialog/llms-full.txt), the dialog box of **Edit Merge Block**, is opened the same way with the `DataSourceManager` and `textControl1`.

### Insert a Paragraph Block in Code

Without a dialog box, [DataSourceManager.InsertMergeBlock](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/insertmergeblock-method/llms-full.txt) inserts a block with the [MergeBlockSettings](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/llms-full.txt) you pass:

| Property | Sets |
|---|---|
| [BlockTemplateType](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/blocktemplatetype-property/llms-full.txt) | `TableRow` for a table row, `PlainParagraph` for paragraphs |
| [CreateHeaderRow](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/createheaderrow-property/llms-full.txt) | Whether a table row block gets a header row |
| [HighlightColor](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/highlightcolor-property/llms-full.txt) | The color of the block while the input position is in it, with its alpha value |
| [FieldDisplayMode](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/fielddisplaymode-property/llms-full.txt) | Whether the fields show their text or their field codes |

The following handler inserts the invoice items as paragraphs instead of table rows. `HighlightColor` is a `System.Drawing.Color`, not a WPF color, so the code names its type in full. It needs `using System.Collections.Generic;`:

```csharp
private void insertItemListButton_Click(object sender, RoutedEventArgs e)
{
    // A paragraph for each item with two of its fields
    MergeBlockInfo itemsBlock = new MergeBlockInfo("Items")
    {
        ColumnNames = new List<string> { "Description", "Quantity" }
    };

    MergeBlockSettings settings = new MergeBlockSettings(BlockTemplateType.PlainParagraph)
    {
        // System.Drawing.Color, also in a WPF application
        HighlightColor = System.Drawing.Color.FromArgb(64, System.Drawing.Color.SteelBlue)
    };

    ribbonReportingTab1.DataSourceManager.InsertMergeBlock(textControl1, itemsBlock, settings);

    if (settings.AddResult != SubTextPartCollection.AddResult.Successful)
    {
        MessageBox.Show(this, "The merge block was not inserted: " + settings.AddResult);
    }
}
```

[MergeBlockInfo](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/llms-full.txt) names the block after the `Items` array, and its [ColumnNames](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/columnnames-property/llms-full.txt) become the merge fields. As with the dialog box, [AddResult](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/addresult-property/llms-full.txt) and [CreatedTable](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/createdtable-property/llms-full.txt) report the result after the call.

### Find the Blocks of a Template

Before a template from a user goes into production, your application can check which blocks it expects. A `MailMerge` connected to the WPF `TextControl` lists them without merging:

```csharp
private void listBlocksButton_Click(object sender, RoutedEventArgs e)
{
    StringBuilder report = new StringBuilder();

    using (MailMerge mailMerge = new MailMerge { TextComponent = textControl1 })
    {
        foreach (string blockName in mailMerge.GetBlockNames())
        {
            string[] fieldNames = mailMerge.GetBlockFieldNames(blockName);
            report.AppendLine(blockName + ": " + string.Join(", ", fieldNames));
        }
    }

    MessageBox.Show(this, report.ToString(), "Merge blocks");
}
```

With `using System.Text;` and `using TXTextControl.DocumentServer;`, the handler shows `Items: Description, Quantity, Price` for the invoice template. [GetBlockNames](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/getblocknames-method/llms-full.txt) returns the block names and [GetBlockFieldNames](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/getblockfieldnames-method/llms-full.txt) the fields of one block.

To find out which block the user is working in, ask for the subtext part at the input position with [SubTextParts.GetItem](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/subtextpartcollection/getitem-method/llms-full.txt):

```csharp
private void currentBlockButton_Click(object sender, RoutedEventArgs e)
{
    // The innermost subtext part at the input position, or null
    SubTextPart subTextPart = textControl1.SubTextParts.GetItem();

    if (subTextPart == null || !DataSourceManager.IsMergeBlock(subTextPart))
    {
        MessageBox.Show(this, "The input position is not in a merge block.");
        return;
    }

    MergeBlockInfo mergeBlockInfo = new MergeBlockInfo(subTextPart);

    MessageBox.Show(this,
        $"Block {mergeBlockInfo.TableName}, level {subTextPart.NestedLevel}, " +
        $"{mergeBlockInfo.MergeFields.Length} merge fields");
}
```

[DataSourceManager.IsMergeBlock](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/ismergeblock-method/llms-full.txt) separates merge blocks from other subtext parts. A `MergeBlockInfo` created from a subtext part reads an existing block: [TableName](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/tablename-property/llms-full.txt) is the name of its data, and [MergeFields](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/mergefields-property/llms-full.txt) its fields without those of nested blocks. [NestedLevel](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/subtextpart/nestedlevel-property/llms-full.txt) is 1 at the top level.

### Rename Blocks and Fields in Code

Block names and prefixed field names must follow the data. When a table or an array of the data gets a new name, every template that uses it needs the new name too. **Edit Merge Block** of the WPF Reporting tab renames the blocks of one template; for many templates, your code renames them. A block is a subtext part named `txmb_` and the block name, and a field of a related table carries the table name before the dot, so both are renamed with string operations. The following method of *MainWindow.xaml.cs* renames a table in the template in `textControl1`:

```csharp
using TXTextControl;
using TXTextControl.DocumentServer;
using TXTextControl.DocumentServer.Fields;
```

```csharp
private void RenameDataTable(string oldName, string newName)
{
    string oldBlockName = MailMerge.MergeBlockNamePrefix + oldName;

    // The main text, text frames, headers and footers
    foreach (IFormattedText textPart in textControl1.TextParts)
    {
        // Merge blocks are subtext parts named txmb_ and the block name
        foreach (SubTextPart subTextPart in textPart.SubTextParts)
        {
            if (subTextPart.Name == oldBlockName)
            {
                subTextPart.Name = MailMerge.MergeBlockNamePrefix + newName;
            }
        }

        // Merge fields that address the table with dot notation, such as Items.Price
        foreach (ApplicationField applicationField in textPart.ApplicationFields)
        {
            if (applicationField.TypeName != "MERGEFIELD")
            {
                continue;
            }

            MergeField mergeField = new MergeField(applicationField);

            if (mergeField.Name.StartsWith(oldName + "."))
            {
                mergeField.Name = newName + mergeField.Name.Substring(oldName.Length);
            }
        }
    }
}
```

The code works as follows:

- [TextParts](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/textparts-property/llms-full.txt) holds the main text, the text frames and the headers and footers. Each part has its own [SubTextParts](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/iformattedtext/subtextparts-property/llms-full.txt) and `ApplicationFields`.
- A subtext part whose [Name](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/subtextpart/name-property/llms-full.txt) is `txmb_Items` is the block `Items`. Setting `Name` renames the block.
- A `MergeField` wrapped around each merge field changes [Name](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-fields/mergefield/name-property/llms-full.txt) in the document. Fields inside the block need no change, because their names don't contain the table name.

`RenameDataTable("Items", "LineItems")` adapts the invoice template to data whose array is named `LineItems`. Save the template afterward. The blog article [Renaming Merge Blocks and Merge Fields Programmatically in C#](https://www.textcontrol.com/blog/2024/02/08/renaming-merge-blocks-and-merge-fields-programmatically-in-csharp/) wraps the same steps in a helper class.

### Use a Block for a Single Object

When a block name matches a single nested object instead of an array, the block is merged once if the object exists. When the object or the array is missing from the data, the block is removed with its content, because [RemoveEmptyBlocks](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/removeemptyblocks-property/llms-full.txt) is `true` by default. [Conditional Content](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/conditional-content/llms-full.txt) builds optional sections on this behavior.

### Follow Blocks During the Merge

[BlockMerging](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/blockmerging-event/llms-full.txt) occurs before a block is merged and passes its data, and [BlockRowMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/blockrowmerged-event/llms-full.txt) occurs after each merged row, which your code can change. Both are described in [Merge Events](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/events/llms-full.txt), and [Formatting and Styling](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/styling/llms-full.txt) uses them to format rows.

### Merge Blocks From a DataSet

A `DataSet` fills blocks through the relations between its tables, when [MailMerge.Merge](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/merge-method/llms-full.txt) is called with the master table. [DataSets and DataTables](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/datasets/#merge-related-tables) explains the setup and what replaces the obsolete `MergeBlocks` method.

### Render a Block as a Numbered List

Records can become the items of a numbered list. Put the merge fields into a numbered list item, add an empty list item below it, select from the fields to the empty item, so that the paragraph end of the first item is included, and make the selection a block. The blog article [Reporting: Merge Blocks Rendered As Numbered Lists](https://www.textcontrol.com/blog/2016/05/05/reporting-merge-blocks-rendered-as-numbered-lists/) walks through such a template.

### Next Steps

- [Nested Blocks and Master-Detail Reports](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/nested-blocks/llms-full.txt): blocks inside blocks for orders and their items.
- [Sorting and Filtering](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/sorting-filtering/llms-full.txt): merge the rows of a block in order and only the rows you need.
- [Conditional Content](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/conditional-content/llms-full.txt): include or leave out sections depending on the data.
- [Template Preview](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-preview/llms-full.txt): show users the merged blocks while they design.

---

## Nearby

- **Previous:** [Merge Fields](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/fields/llms-full.txt)
- **Next:** [Nested Blocks and Master-Detail Reports](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/nested-blocks/llms-full.txt)
