# Merge Blocks

> Merge blocks in depth: content, DOCX storage, paragraph and table row blocks with InsertMergeBlock, single objects, GetBlockNames, block events and DataSets.

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

---

Anything that repeats in a document, from the line items of an invoice to a list of contract clauses, is a merge block. A block can also stand for a single object and disappear when the data doesn't contain it. You decide what goes into a block, how blocks are stored in DOCX templates, which kind of block your code inserts, how to list the blocks of a template and how to follow them during a merge.

[Merge Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/mail-merge/blocks/llms-full.txt) in Getting Started introduces blocks: how a block name matches the data, how to create blocks in the Document Editor and with `DataSourceManager`, and a first sort and filter instruction. Blocks inside blocks are the subject of [Nested Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/nested-blocks/llms-full.txt). *data.json* holds the invoice data, and *template.tx* is the template with a `LineItems` block that the Getting Started page creates.

### What a Merge Block Contains

A merge block can contain a table row, a complete table, paragraphs, images or text frames, and even a single word. `MailMerge` repeats the whole block for each record, with its formatting:

- **Breaks are repeated.** A paragraph end inside the block starts a new paragraph for each record, and a page break, section break or column break in the block is inserted for each record.
- **Each record on a new page.** With `ParagraphFormat.PageBreakBefore` set for the first paragraph of the block, every repetition starts on a new page.
- **Table headers.** A header row above a table row block is repeated with the content it had first; [Repeat Table Headers per Block](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/nested-blocks/#repeat-table-headers-per-block) gives each block a header of its own.
- **Fields.** Merge fields in the block receive the values of the current record. Images and barcodes in a block are merged for each record too.

### How Blocks Are Stored

In a document, a merge block is a [SubTextPart](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/subtextpart/llms-full.txt) whose name is `txmb_` followed by the block name; [MailMerge.MergeBlockNamePrefix](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge/mergeblocknameprefix-property/llms-full.txt) returns the prefix. What happens in other formats:

- **DOCX, DOC and RTF** templates are loaded with [LoadSettings.LoadSubTextParts](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/loadsettings/loadsubtextparts-property/llms-full.txt) set to `true`. It converts bookmarks that extend over several characters into subtext parts, so that the merge blocks are loaded; without it, bookmarks become document targets.
- **Older templates** mark a block with two document targets, `blockstart_` and `blockend_` followed by the block name. They can still be loaded.
- **When saving,** [ReportingMergeBlockFormat](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/loadsavesettingsbase/reportingmergeblockformat-property/llms-full.txt) of `SaveSettings` decides how blocks are written: `Default` saves them as they are, and `DocumentTargets` converts subtext part blocks into the older document targets.

Sort and filter instructions are stored with the block in the TX Text Control format, as [Sorting and Filtering](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/sorting-filtering/#where-the-instructions-are-stored) explains.

### Insert Paragraph and Table Row Blocks

[DataSourceManager.InsertMergeBlock](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/datasourcemanager/insertmergeblock-method/llms-full.txt) inserts a block at the input position. The [MergeBlockSettings](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/llms-full.txt) you pass decide how it looks:

| Property | Sets |
|---|---|
| [BlockTemplateType](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/blocktemplatetype-property/llms-full.txt) | `TableRow` for a table row block or `PlainParagraph` for a paragraph block |
| [CreateHeaderRow](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/createheaderrow-property/llms-full.txt) | Whether a table header row is generated for a table row block |
| [HighlightColor](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/highlightcolor-property/llms-full.txt) | The color that highlights the block in an editor while the input position is inside it |
| [FieldDisplayMode](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/fielddisplaymode-property/llms-full.txt) | Whether the fields show their text or their field codes |
| [TableID](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/tableid-property/llms-full.txt) | The ID of the table that a table row block creates |

After the call, [AddResult](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/addresult-property/llms-full.txt) tells whether the block was inserted, and [CreatedTable](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/createdtable-property/llms-full.txt) returns the table of a table row block, for example to format it. The following program inserts a paragraph block for the customer of an invoice and merges it:

```csharp
using TXTextControl;
using TXTextControl.DocumentServer;
using TXTextControl.DocumentServer.DataSources;

string jsonData = File.ReadAllText("data.json");

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    DataSourceManager dataSourceManager = new DataSourceManager();
    dataSourceManager.LoadJson(jsonData);

    // A block for the nested Customer object with its two fields
    MergeBlockInfo customerBlock = new MergeBlockInfo("Customer")
    {
        ColumnNames = new List<string> { "CustomerName", "CustomerAddress" }
    };

    // Paragraphs instead of a table row
    MergeBlockSettings settings = new MergeBlockSettings(BlockTemplateType.PlainParagraph);

    dataSourceManager.InsertMergeBlock(tx, customerBlock, settings);

    if (settings.AddResult != SubTextPartCollection.AddResult.Successful)
    {
        Console.WriteLine($"The merge block was not inserted: {settings.AddResult}");
        return;
    }

    using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
    {
        mailMerge.MergeJsonData(jsonData);
    }

    tx.Save("customer.pdf", StreamType.AdobePDF);
}
```

The code works as follows:

- The [MergeBlockInfo](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/llms-full.txt) describes the block: its name, which matches `Customer` in the data, and in [ColumnNames](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/columnnames-property/llms-full.txt) the merge fields it contains.
- The [MergeBlockSettings constructor](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblocksettings/constructor/llms-full.txt) with `BlockTemplateType.PlainParagraph` inserts the fields as paragraphs. With `BlockTemplateType.TableRow` and `true` as second parameter, it inserts a table with a header row.
- `AddResult` is a value of [SubTextPartCollection.AddResult](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/subtextpartcollection/addresult-enumeration/llms-full.txt), such as `Successful` or `Overlapping`.

### Use Blocks for Single Objects

A merge block doesn't have to repeat. The `Customer` block above is bound to a single object: it's merged once when the object exists in the data and removed when it's missing. [Include Optional Sections](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/conditional-content/#include-optional-sections) in Conditional Content builds optional sections of a document this way.

### List the Blocks of a Template

Before merging a template from another source, you may want to know which blocks and fields it expects. The following program lists them for *template.docx*:

```csharp
using TXTextControl;
using TXTextControl.DocumentServer;
using TXTextControl.DocumentServer.DataSources;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    LoadSettings loadSettings = new LoadSettings()
    {
        ApplicationFieldFormat = ApplicationFieldFormat.MSWord,
        LoadSubTextParts = true
    };

    tx.Load("template.docx", StreamType.WordprocessingML, loadSettings);

    using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
    {
        foreach (string blockName in mailMerge.GetBlockNames())
        {
            string[] fieldNames = mailMerge.GetBlockFieldNames(blockName);
            Console.WriteLine($"{blockName}: {string.Join(", ", fieldNames)}");
        }
    }

    foreach (SubTextPart subTextPart in tx.SubTextParts)
    {
        if (DataSourceManager.IsMergeBlock(subTextPart))
        {
            MergeBlockInfo mergeBlockInfo = new MergeBlockInfo(subTextPart);
            Console.WriteLine($"{subTextPart.Name} (level {subTextPart.NestedLevel}): table {mergeBlockInfo.TableName}, {mergeBlockInfo.MergeFields.Length} fields");
        }
    }
}
```

The code works as follows:

- [GetBlockNames](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge/getblocknames-method/llms-full.txt) returns the names of all merge blocks of the loaded template, and [GetBlockFieldNames](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge/getblockfieldnames-method/llms-full.txt) the names of the merge fields in a block.
- [DataSourceManager.IsMergeBlock](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/datasourcemanager/ismergeblock-method/llms-full.txt) tells whether a subtext part is a merge block.
- The [MergeBlockInfo constructor](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/constructor/llms-full.txt) with a subtext part reads an existing block. [TableName](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/tablename-property/llms-full.txt) is the name of its data table, and [MergeFields](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-datasources/mergeblockinfo/mergefields-property/llms-full.txt) contains its merge fields without those of nested blocks.
- [SubTextPart.NestedLevel](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/subtextpart/nestedlevel-property/llms-full.txt) is 1 for a block at the top level.

### Follow Blocks During the Merge

Two events accompany the merge of a block. [BlockMerging](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge/blockmerging-event/llms-full.txt) occurs before a block is merged, and [BlockRowMerged](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge/blockrowmerged-event/llms-full.txt) after each of its rows:

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

string jsonData = File.ReadAllText("data.json");

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Load("template.tx", StreamType.InternalUnicodeFormat);

    using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
    {
        mailMerge.BlockMerging += (sender, e) =>
        {
            Console.WriteLine($"Merging block {e.BlockName} with {e.BlockData?.Length ?? 0} rows");
        };

        mailMerge.BlockRowMerged += (sender, e) =>
        {
            Console.WriteLine($"{e.MergeBlockName}: row {e.DataRowNumber} of {e.DataRowCount}");
        };

        mailMerge.MergeJsonData(jsonData);
    }

    tx.Save("results.pdf", StreamType.AdobePDF);
}
```

- [BlockMergingEventArgs](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockmergingeventargs/llms-full.txt) has the [BlockName](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockmergingeventargs/blockname-property/llms-full.txt), the [ColumnNames](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockmergingeventargs/columnnames-property/llms-full.txt) and the rows of the block in [BlockData](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockmergingeventargs/blockdata-property/llms-full.txt).
- [BlockRowMergedEventArgs](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/llms-full.txt) has the [MergeBlockName](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/mergeblockname-property/llms-full.txt), the [DataRowNumber](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/datarownumber-property/llms-full.txt) of the merged row, the [DataRowCount](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/datarowcount-property/llms-full.txt) of the block and the merged row itself in [MergedBlockRow](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/mergedblockrow-property/llms-full.txt), a byte array in the TX Text Control format.

To change a merged row, load `MergedBlockRow` into a second `ServerTextControl`, change the content and assign the saved bytes back. [Styling](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/styling/llms-full.txt) uses this to color every second row, and the blog article [Using MailMerge Events to Remove Trailing Text in a Merge Block](https://www.textcontrol.com/blog/2022/11/07/using-mailmerge-events-to-remove-trailing-text-in-a-merge-block/) removes a separator after the last row.

### Merge Blocks With a DataSet

To fill merge blocks from a `DataSet`, name each table like the block it fills, connect it to a master table with a `DataRelation` and pass the master table to [MailMerge.Merge](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge/merge-method/llms-full.txt). `Merge` fills each block with the related table of the same name and the fields outside the blocks with the master table, as [Data Sources](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/data-sources/#datatable-and-dataset) shows, together with the obsolete `MergeBlocks` method that older code may call.

### Render a Block as a Numbered List

A block doesn't need a table. To merge records as a numbered list, put the merge fields into a numbered list item, add an empty list item after it and make the first item, including its paragraph end, the block. Each record then becomes a numbered item of the list. 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/) shows the template.

### Next Steps

- [Nested Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/nested-blocks/llms-full.txt): merge master-detail data with blocks inside blocks.
- [Sorting and Filtering](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/sorting-filtering/llms-full.txt): merge the rows of a block in order and only the rows you need.
- [Styling](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/styling/llms-full.txt): format merged rows.
- [Subtext Parts](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/subtext-parts/llms-full.txt): the document element that blocks are made of.

---

## Nearby

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