# Merge Events

> Use the .NET events of MailMerge in WPF: FieldMerged, BlockMerging, BlockRowMerged, DataRowMerged and more, their order, a merge log and Cancel.

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

---

Every step of a merge can be observed from your code. `MailMerge` raises an event before each merge block, after each field, block row and record, and when images, barcodes, charts, form fields and subtemplates are merged. In a WPF application, these are ordinary .NET events of a class that your code creates, not routed events of a XAML element, so handlers are attached with `+=` in *MainWindow.xaml.cs*. Below you find each event, the order in which they occur, a log that makes the order visible and a handler that cancels a merge.

What the events are used for is shown in context elsewhere: [Formatting and Styling](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/styling/llms-full.txt) colors rows and cells, [Merge Fields](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/fields/llms-full.txt) changes the text of fields, and the [Overview](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/overview/#create-one-document-or-one-per-record) saves each record as a document. The examples merge the invoice of the Getting Started tutorials, *invoice.docx* and *invoice.json* with its `Items` block.

### The Events of MailMerge

| Event | Occurs | Its arguments provide |
|---|---|---|
| [FieldMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/fieldmerged-event/llms-full.txt) | For each field, whether it received data or not | The field, `Merged`, the record, the block, the table cell and the merged field as a byte array; `Cancel` |
| [BlockMerging](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/blockmerging-event/llms-full.txt) | Before a merge block is merged | The block name, its column names and its rows; `Cancel` |
| [BlockRowMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/blockrowmerged-event/llms-full.txt) | After each row of a merge block | The block name, the row number, the row count and the merged row as a byte array |
| [DataRowMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/datarowmerged-event/llms-full.txt) | After each record of the master table | The record, its number and its merged document as a byte array; `Cancel` |
| [IncludeTextMerging](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/includetextmerging-event/llms-full.txt) | When an `INCLUDETEXT` field is merged | The file name and the field; your code can supply the document to include |
| [ImageMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/imagemerged-event/llms-full.txt) | After an image placeholder has been merged | The image, its name and its descriptive text, which can be replaced |
| [ImageFieldMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/imagefieldmerged-event/llms-full.txt) | After a merge field with the prefix `image:` has been merged | The field name and the merged image, which can be replaced |
| [BarcodeMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/barcodemerged-event/llms-full.txt), [ChartMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/chartmerged-event/llms-full.txt) | After a barcode or a chart has been merged | The merged `BarcodeFrame` or `ChartFrame` and the record |
| [FormFieldMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/formfieldmerged-event/llms-full.txt) | After a form field has been merged | The form field, `Merged`, the record and the table cell |

The arguments locate the merged element in the template: `MergeBlockName` is the name of its merge block, or an empty string outside blocks, and `NestingDepth` is the level of that block. `SubTextPartID` and `SubTextPartNumber`, added in version 33.0, identify the subtext part of the block. The events of images, barcodes and subtemplates are covered in [Images and Barcodes](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/images-barcodes/llms-full.txt) and [Subtemplates](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/subtemplates/llms-full.txt).

### The Order of the Events

Within each record of the master table, a merge block and the record raise their events in this order:

```text
Record of the master table
 |- BlockMerging          before a merge block is merged
 |   |- FieldMerged       for each field of a block row
 |   '- BlockRowMerged    after each row of the block
 '- DataRowMerged         after the record has been merged
```

Fields outside blocks raise `FieldMerged` too. A log shows the order for your own template. Add a button and a `ListBox` to the `DockPanel` of *MainWindow.xaml*:

```xml
<Button x:Name="logMergeButton" DockPanel.Dock="Top" Content="Merge With Log"
        Click="logMergeButton_Click" />
<ListBox x:Name="mergeLog" DockPanel.Dock="Bottom" Height="150" />
```

The handler in *MainWindow.xaml.cs* merges the template that is loaded in `textControl1` and writes each event into the list:

```csharp
using System.IO;
using System.Windows;
using TXTextControl.DocumentServer;
using TXTextControl.DocumentServer.Fields;
```

```csharp
private void logMergeButton_Click(object sender, RoutedEventArgs e)
{
    mergeLog.Items.Clear();

    using (MailMerge mailMerge = new MailMerge { TextComponent = textControl1 })
    {
        mailMerge.BlockMerging += (s, args) =>
            mergeLog.Items.Add($"BlockMerging: {args.BlockName}, {args.BlockData.Length} rows");

        mailMerge.FieldMerged += (s, args) =>
        {
            if (args.MailMergeFieldAdapter is MergeField mergeField)
            {
                string block = args.MergeBlockName == "" ? "" : $" in {args.MergeBlockName}";
                mergeLog.Items.Add($"FieldMerged: {mergeField.Name}{block}, merged: {args.Merged}");
            }
        };

        mailMerge.BlockRowMerged += (s, args) =>
            mergeLog.Items.Add($"BlockRowMerged: {args.MergeBlockName} row {args.DataRowNumber} of {args.DataRowCount}");

        mailMerge.DataRowMerged += (s, args) =>
            mergeLog.Items.Add($"DataRowMerged: record {args.DataRowNumber}");

        // Merge the template in the editor; the result replaces it
        mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
    }
}
```

The code works as follows:

- `MailMerge` is created in code and connected to the editor with [TextComponent](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/textcomponent-property/llms-full.txt). The handlers are attached before [MergeJsonData](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/mergejsondata-method/llms-full.txt) starts the merge.
- [BlockName](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-blockmergingeventargs/blockname-property/llms-full.txt) and [BlockData](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-blockmergingeventargs/blockdata-property/llms-full.txt) name the block and hold its rows before they're merged.
- [MailMergeFieldAdapter](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/mailmergefieldadapter-property/llms-full.txt) is the merged field, a `MergeField` for merge fields, and [MergeBlockName](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/mergeblockname-property/llms-full.txt) is empty outside blocks.
- [DataRowNumber](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/datarownumber-property/llms-full.txt) and [DataRowCount](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/datarowcount-property/llms-full.txt) count the rows of a block, and the [DataRowNumber](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/datarownumber-property/llms-full.txt) of `DataRowMerged` counts the records.

The log lists the `Items` block with its two rows, the fields of each row and the single record, and the editor shows the merged invoice. Load the template again before the next merge.

### Cancel a Merge

`FieldMerged` and `DataRowMerged` can stop a merge: their arguments have a `Cancel` property. The following handler merges in a `ServerTextControl` and saves the PDF only when every field outside the blocks receives data:

```csharp
using System.IO;
using System.Windows;
using TXTextControl;
using TXTextControl.DocumentServer;
using TXTextControl.DocumentServer.Fields;
```

```csharp
private void checkedMergeButton_Click(object sender, RoutedEventArgs e)
{
    string missingField = null;

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

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

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

        using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
        {
            mailMerge.FieldMerged += (s, args) =>
            {
                // Stop at the first field outside a block without data
                if (!args.Merged && args.MergeBlockName == "" &&
                    args.MailMergeFieldAdapter is MergeField mergeField)
                {
                    missingField = mergeField.Name;
                    args.Cancel = true;
                }
            };

            mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
        }

        if (missingField != null)
        {
            MessageBox.Show(this, $"The merge was canceled: no data for {missingField}.");
            return;
        }

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

[Cancel](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/cancel-property/llms-full.txt) of `FieldMergedEventArgs` cancels the merge at a field, and [Cancel](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/cancel-property/llms-full.txt) of `DataRowMergedEventArgs` after a complete record. A merge canceled through `DataRowMergedEventArgs.Cancel` ends with a [MergeCanceledException](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mergecanceledexception/llms-full.txt), which the code that calls the merge method catches. [Merged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/merged-property/llms-full.txt) is `false` for a field without data. To try it, remove *Company* from *invoice.json*. `BlockMergingEventArgs` derives from `CancelEventArgs` and can cancel the merge of one block.

### Change Merged Content

[MergedField](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/mergedfield-property/llms-full.txt) of `FieldMerged`, [MergedBlockRow](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/mergedblockrow-property/llms-full.txt) of `BlockRowMerged` and [MergedRow](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/mergedrow-property/llms-full.txt) of `DataRowMerged` contain merged content as a byte array in the internal TX Text Control format. A handler loads it into a second `ServerTextControl`, changes it and assigns the saved bytes back, as [Formatting and Styling](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/styling/#alternate-row-colors-in-code) does with block rows.

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/) uses `BlockMerging`, `FieldMerged` and `BlockRowMerged` together to remove the separator after the last row of a list, and [MailMerge: Merging Hyperlinks using the FieldMerged Event](https://www.textcontrol.com/blog/2022/06/08/mailmerge-merging-hyperlinks-using-the-fieldmerged-event/) creates hyperlinks from merged values.

### Skip Records During the Merge

`MailMerge` merges every record it receives, so the natural place to leave records out is your code that prepares the data. When the decision can only be made during the merge, for example from input of the user, replace the document of a record with an empty one in `DataRowMerged`. The following handler of *MainWindow.xaml.cs* merges *invoice.json* and leaves out the record with the number 1:

```csharp
using System.Collections.Generic;
using System.IO;
using TXTextControl;
using TXTextControl.DocumentServer;
```

```csharp
private void mergeSkippingButton_Click(object sender, RoutedEventArgs e)
{
    // The numbers of the records to leave out; the first record has the number 0
    HashSet<int> recordsToSkip = new HashSet<int> { 1 };

    // An empty document that replaces each skipped record
    byte[] emptyDocument;

    using (ServerTextControl empty = new ServerTextControl())
    {
        empty.Create();
        empty.Save(out emptyDocument, BinaryStreamType.InternalUnicodeFormat);
    }

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

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

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

        using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
        {
            mailMerge.DataRowMerged += (s, args) =>
            {
                if (recordsToSkip.Contains(args.DataRowNumber))
                {
                    args.MergedRow = emptyDocument;
                }
            };

            mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
        }

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

The code works as follows:

- The empty document is created once, before the merge, and saved in the internal TX Text Control format that `MergedRow` expects.
- [DataRowNumber](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/datarownumber-property/llms-full.txt) counts the records from 0, so the number 1 is the second invoice. Assigning the empty document to [MergedRow](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/mergedrow-property/llms-full.txt) removes that invoice from the result.
- To decide by the content of a record instead of its number, read its values from [DataRow](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/datarow-property/llms-full.txt), as [Conditional Content](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/conditional-content/#change-a-merged-value-in-code) does in `FieldMerged`.

Add two more invoices to the array of *invoice.json* to see the effect: *invoices.pdf* then contains the first and the third invoice. The blog article [Mail Merge: Skipping Records During the Merge Process in .NET C#](https://www.textcontrol.com/blog/2025/02/10/mail-merge-skipping-records-during-the-merge-process-in-net-c-sharp/) uses the same technique with a list of record numbers.

### Events of the DataSourceManager

The `DataSourceManager` of the **Reporting** tab has a [Merge](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/merge-method/llms-full.txt) method of its own, used for previews, and raises [FieldMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/fieldmerged-event/llms-full.txt) and [DataRowMerged](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/datarowmerged-event/llms-full.txt) with the arguments of the `MailMerge` events. See [Template Preview](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-preview/llms-full.txt).

### Next Steps

- [Formatting and Styling](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/styling/llms-full.txt): format rows, cells and text in the events.
- [Merge Fields](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/fields/llms-full.txt): change field text and handle fields without data.
- [Merge Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/blocks/llms-full.txt): the blocks whose rows the events report.
- [Template Design](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-design/llms-full.txt): test a template with `FieldMerged`.

---

## Nearby

- **Previous:** [Template Preview](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/template-preview/llms-full.txt)
- **Next:** [PDF Processing](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/pdf/llms-full.txt)
