# Merge Events

> Handle MailMerge events in Windows Forms: FieldMerged, BlockMerging, BlockRowMerged and DataRowMerged, their order, designer handlers and Cancel.

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

---

`MailMerge` reports its progress through events: before a merge block is merged, after each field, each block row and each record, and when images, barcodes, charts, form fields and subtemplates are merged. Handlers of these events check the data, change the merged content, collect results or stop the merge. All events of `MailMerge` are listed below with the order in which they occur, how a Windows Forms handler is created in the designer and how a handler cancels a merge.

Other pages show what the events are good for: [Formatting and Styling](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/styling/llms-full.txt) colors rows and cells, [Merge Fields](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/fields/llms-full.txt) changes field text, and [Overview](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/overview/#create-one-document-or-one-per-record) saves one document per record. The examples use the invoice of the Getting Started tutorials, *invoice.docx* and *invoice.json* with the `Items` block.

### The Events of MailMerge

| Event | Occurs | Its arguments provide |
|---|---|---|
| [FieldMerged](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/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-winforms/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-winforms/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-winforms/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-winforms/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-winforms/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-winforms/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-winforms/reference/txtextcontrol-documentserver/mailmerge/barcodemerged-event/llms-full.txt), [ChartMerged](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/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-winforms/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 |

Most arguments tell where in the template an element is: `MergeBlockName` is the name of its merge block, or an empty string outside blocks, and `NestingDepth` the level of the block. Since version 33.0, `SubTextPartID` and `SubTextPartNumber` identify the subtext part of the block. [Images and Barcodes](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/images-barcodes/llms-full.txt) and [Subtemplates](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/subtemplates/llms-full.txt) describe the events of their elements.

### The Order of the Events

For each record of the master table, the events of a merge block and the record occur 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` as well. To watch the order with your own template, log the events. The following handler of a button named `logMergeButton` merges the invoice in a `ServerTextControl` and writes each event into a `ListBox` named `listBox1`:

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

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

    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.BlockMerging += (s, args) =>
                listBox1.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}";
                    listBox1.Items.Add($"FieldMerged: {mergeField.Name}{block}, merged: {args.Merged}");
                }
            };

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

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

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

The code works as follows:

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

The list shows the block `Items` with its two rows, the fields of each row and one record.

### Create Handlers in the Designer

A `MailMerge` component dropped on the form, `mailMerge1` in the component tray, has its events in the **Properties** window. Select the component, click the **Events** button with the lightning icon at the top of the window and double-click an event, such as **FieldMerged**. Visual Studio creates the handler in *Form1.cs* and connects it in *Form1.Designer.cs*:

```csharp
private void mailMerge1_FieldMerged(object sender, MailMerge.FieldMergedEventArgs e)
{
    if (!e.Merged && e.MailMergeFieldAdapter is MergeField mergeField)
    {
        listBox1.Items.Add($"No data for {mergeField.Name}");
    }
}
```

With its [TextComponent](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver/mailmerge/textcomponent-property/llms-full.txt) set to `textControl1`, `mailMerge1.MergeJsonData(File.ReadAllText("invoice.json"))` merges the document in the editor, and the handler runs for each of its fields. [Overview](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/overview/#where-the-merge-runs) shows how to set up the component.

### Cancel a Merge

The arguments of `FieldMerged` and `DataRowMerged` have a `Cancel` property that cancels the current merge. The following handler merges the invoice only when every field outside the blocks receives data, and saves no PDF otherwise:

```csharp
private void checkedMergeButton_Click(object sender, EventArgs 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-winforms/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/cancel-property/llms-full.txt) of `FieldMergedEventArgs` and [Cancel](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/cancel-property/llms-full.txt) of `DataRowMergedEventArgs` both cancel the merge; the second one fits checks that need the complete record. A merge canceled through `DataRowMergedEventArgs.Cancel` ends with a [MergeCanceledException](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/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-winforms/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/merged-property/llms-full.txt) is `false` for a field that received no data. Remove *Company* from *invoice.json* to see the message. `BlockMergingEventArgs` derives from `CancelEventArgs` and can cancel the merge of a single block.

### Change Merged Content

Three arguments hand over merged content as a byte array in the internal TX Text Control format: [MergedField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver/mailmerge-fieldmergedeventargs/mergedfield-property/llms-full.txt) of `FieldMerged`, [MergedBlockRow](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver/mailmerge-blockrowmergedeventargs/mergedblockrow-property/llms-full.txt) of `BlockRowMerged` and [MergedRow](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/mergedrow-property/llms-full.txt) of `DataRowMerged`. Load the bytes into a second `ServerTextControl`, change the content and assign the saved bytes back; the merge continues with the changed content. [Formatting and Styling](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/styling/#alternate-row-colors-in-code) colors block rows this way.

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/) combines `BlockMerging`, `FieldMerged` and `BlockRowMerged` to drop 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/) turns merged values into hyperlinks.

### 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 *Form1.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, EventArgs 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-winforms/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-winforms/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-winforms/reference/txtextcontrol-documentserver/mailmerge-datarowmergedeventargs/datarow-property/llms-full.txt), as [Conditional Content](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/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 merges with its own [Merge](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver-datasources/datasourcemanager/merge-method/llms-full.txt) method, for example for a preview. It raises [FieldMerged](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver-datasources/datasourcemanager/fieldmerged-event/llms-full.txt) and [DataRowMerged](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver-datasources/datasourcemanager/datarowmerged-event/llms-full.txt) with the same arguments as `MailMerge`; [Template Preview](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/template-preview/llms-full.txt) uses this method.

### Next Steps

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

---

## Nearby

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