Customize Merge

Mail merging in GemBox.Document is very flexible and customizable. You can customize the MailMerge.FieldMerging event to suit your data importing requirements.

Your event handler has access to a FieldMergingEventArgs object for each merging field. With it you can change the value or the format of the data that's being inserted, or replace the Run element's insertion with something else like Picture or Hyperlink elements.

Note, there is an easier way to import Picture elements in mail merge process, see the Merge Pictures example. Also, you can format the Date and Time or Numeric values by simply using the formatting switches on the MERGEFIELD element itself (\@ for dates and \# for numbers).

The following example shows how you can perform a customized mail merge.

Word document generated from customizable mail merge
Screenshot of Word file created with customized merge
Upload your file (Drag file here)
using GemBox.Document;

class Program
{
    static void Main()
    {
        // If using Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");

        var document = DocumentModel.Load("%InputFileName%");

        document.MailMerge.FieldMerging += (sender, e) =>
        {
            if (e.IsValueFound && e.Value != null)
            {
                switch (e.FieldName)
                {
                    case "CheckedField":
                        bool checkedValue = (bool)e.Value;
                        var run = (Run)e.Inline;
                        run.CharacterFormat.FontColor = checkedValue ? Color.Green : Color.Red;
                        run.Text = checkedValue ? "☑" : "☒";
                        break;

                    case "LinkField":
                        var linkValue = ((string Address, string DisplayText))e.Value;
                        e.Inline = new Hyperlink(e.Document, linkValue.Address, linkValue.DisplayText);
                        break;

                    case "ImageField":
                        var imagePath = e.Value.ToString();
                        e.Inline = new Picture(e.Document, imagePath);
                        break;
                }
            }
        };

        document.MailMerge.Execute(
            new
            {
                CheckedField = true,
                LinkField = (Address: "https://www.gemboxsoftware.com/", DisplayText: "GemBox Homepage"),
                ImageField = "%#Dices.png%"
            });

        document.Save("Merged Customizations Output.%OutputFileType%");
    }
}
Imports GemBox.Document

Module Program

    Sub Main()

        ' If using Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")

        Dim document = DocumentModel.Load("%InputFileName%")

        AddHandler document.MailMerge.FieldMerging,
            Sub(sender, e)
                If e.IsValueFound And e.Value IsNot Nothing Then
                    Select Case e.FieldName

                        Case "CheckedField"
                            Dim checkedValue As Boolean = DirectCast(e.Value, Boolean)
                            Dim run = DirectCast(e.Inline, Run)
                            run.CharacterFormat.FontColor = If(checkedValue, Color.Green, Color.Red)
                            run.Text = If(checkedValue, "☑", "☒")
                            Exit Select

                        Case "LinkField"
                            Dim linkValue = DirectCast(e.Value, (Address As String, DisplayText As String))
                            e.Inline = New Hyperlink(e.Document, linkValue.Address, linkValue.DisplayText)
                            Exit Select

                        Case "ImageField"
                            Dim imagePath = e.Value.ToString()
                            e.Inline = New Picture(e.Document, imagePath)
                            Exit Select

                    End Select
                End If
            End Sub

        document.MailMerge.Execute(
            New With
            {
                .CheckedField = True,
                .LinkField = (Address:="https://www.gemboxsoftware.com/", DisplayText:="GemBox Homepage"),
                .ImageField = "%#Dices.png%"
            })

        document.Save("Merged Customizations Output.%OutputFileType%")

    End Sub
End Module

Insert HTML, RTF, and DOCX with mail merge

GemBox.Document also provides a MailMerge.FieldMappings dictionary with which you can define custom mappings of field names to data source names.

By combining the custom mapping and merging of the mail merge process, you can define your type of MERGEFIELD elements. For instance, you can use an arbitrary prefix in the names of your MERGEFIELD elements and then handle merging those fields as needed.

The following example shows how you can define and use MERGEFIELD elements that insert HTML, RTF, or DOCX content in the mail merge process.

  • MERGEFIELD with the "Html:" prefix will insert an HTML formatted text from the provided source.
  • MERGEFIELD with the "Rtf:" prefix will insert an RTF formatted text from the provided source.
  • MERGEFIELD with the "Docx:" prefix will insert a DOCX file's section from the provided source.
Word document generated from importing HTML, RTF and DOCX with mail merge
Screenshot of inserted HTML, RTF and DOCX content in the mail merge process
Upload your file (Drag file here)
using GemBox.Document;

class Program
{
    static void Main()
    {
        // If using Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");

        var document = DocumentModel.Load("%InputFileName%");

        // Map merge fields with prefix to data source without prefix.
        // E.g. "Html:MyName" field's name to "MyName" data source name.
        foreach (string fieldName in document.MailMerge.GetMergeFieldNames())
        {
            int index = fieldName.IndexOf(':');
            if (index > 0 && !document.MailMerge.FieldMappings.ContainsKey(fieldName))
                document.MailMerge.FieldMappings.Add(fieldName, fieldName.Substring(index + 1));
        }

        // Customize mail merge to support our custom prefixes.
        document.MailMerge.FieldMerging += (sender, e) =>
        {
            if (!e.IsValueFound)
                return;

            bool customImport = true;

            if (e.FieldName.StartsWith("Html:"))
                e.Field.Content.End.LoadText((string)e.Value, LoadOptions.HtmlDefault);
            else if (e.FieldName.StartsWith("Rtf:"))
                e.Field.Content.End.LoadText((string)e.Value, LoadOptions.RtfDefault);
            else if (e.FieldName.StartsWith("Docx:"))
                e.Field.Content.End.InsertRange(DocumentModel.Load((string)e.Value).Sections[0].Blocks.Content);
            else
                customImport = false;

            if (customImport)
            {
                // Remove the default import.
                e.Inline = null;

                // Check if the content is inserted into the same parent paragraph or added after it.
                // This depends on whether the data source contains inline-level or block-level elements.
                // If it's added after, then remove the empty parent which used to contain merge field.
                if (e.Field.ParentCollection.Count == 1)
                    e.Field.Parent.Content.Delete();
            }
        };

        document.MailMerge.Execute(
            new
            {
                Field1 = @"{\rtf1\ansi\deff0{\fonttbl{\f0 Arial Black;}}{\colortbl ;\red255\green128\blue64;}\f0\cf1 This is rich formatted text (in RTF format).}",
                Field2 = "<p style='font-family:Arial Narrow;color:royalblue;'>This is another rich formatted text (in HTML format).</p>",
                Field3 = "<p style='font-family:Arial Narrow;color:seagreen;'>And another rich formatted text (in HTML format).</p>",
                Field4 = "%#Reading.docx%"
            });

        document.Save("Merged Custom Types Output.%OutputFileType%");
    }
}
Imports GemBox.Document

Module Program

    Sub Main()

        ' If using Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")

        Dim document = DocumentModel.Load("%InputFileName%")

        ' Map merge fields with prefix to data source without prefix.
        ' E.g. "Html:MyName" field's name to "MyName" data source name.
        For Each fieldName As String In document.MailMerge.GetMergeFieldNames()
            Dim index As Integer = fieldName.IndexOf(":"c)
            If index > 0 AndAlso Not document.MailMerge.FieldMappings.ContainsKey(fieldName) Then
                document.MailMerge.FieldMappings.Add(fieldName, fieldName.Substring(index + 1))
            End If
        Next

        ' Customize mail merge to support our custom prefixes.
        AddHandler document.MailMerge.FieldMerging,
            Sub(sender, e)
                If Not e.IsValueFound Then Return

                Dim customImport As Boolean = True

                If e.FieldName.StartsWith("Html:") Then
                    e.Field.Content.End.LoadText(CStr(e.Value), LoadOptions.HtmlDefault)
                ElseIf e.FieldName.StartsWith("Rtf:") Then
                    e.Field.Content.End.LoadText(CStr(e.Value), LoadOptions.RtfDefault)
                ElseIf e.FieldName.StartsWith("Docx:") Then
                    e.Field.Content.End.InsertRange(DocumentModel.Load(CStr(e.Value)).Sections(0).Blocks.Content)
                Else
                    customImport = False
                End If

                If customImport Then
                    ' Remove the default import.
                    e.Inline = Nothing

                    ' Check if the content is inserted into the same parent paragraph or added after it.
                    ' This depends on whether the data source contains inline-level or block-level elements.
                    ' If it's added after, then remove the empty parent which used to contain merge field.
                    If e.Field.ParentCollection.Count = 1 Then e.Field.Parent.Content.Delete()
                End If
            End Sub

        document.MailMerge.Execute(
            New With
            {
                .Field1 = "{\rtf1\ansi\deff0{\fonttbl{\f0 Arial Black;}}{\colortbl ;\red255\green128\blue64;}\f0\cf1 This is rich formatted text (in RTF format).}",
                .Field2 = "<p style='font-family:Arial Narrow;color:royalblue;'>This is another rich formatted text (in HTML format).</p>",
                .Field3 = "<p style='font-family:Arial Narrow;color:seagreen;'>And another rich formatted text (in HTML format).</p>",
                .Field4 = "%#Reading.docx%"
            })

        document.Save("Merged Custom Types Output.%OutputFileType%")

    End Sub
End Module

When using any ContentRange.LoadText overload method in which you explicitly specify the formatting that should be used, like HtmlLoadOptions, loaded content will be generated from the provided input text and format. In other words, the inserted content is created based only on the provided HTML and CSS text.

However, in some cases you may want your imported content to inherit the formatting of the MERGEFIELD element. For this, you can specify the default style that should be used, as shown in the next example.

using GemBox.Document;

class Program
{
    static void Main()
    {
        // ...
        document.MailMerge.FieldMerging += (sender, e) =>
        {
            // ...
            if (e.FieldName.StartsWith("Html:"))
            {
                CharacterFormat format = e.Field.CharacterFormat;

                // Use merge field's formatting as default CSS.
                string html = (string)e.Value;
                string htmlStyle = string.Format("<style>* {{ font-family:{0}; font-size:{1}pt; font-weight:{2}; font-style:{3}; color:{4}; }}</style>",
                    format.FontName,
                    format.Size,
                    format.Bold ? "bold" : "normal",
                    format.Italic ? "italic" : "normal",
                    $"#{format.FontColor.R:X2}{format.FontColor.G:X2}{format.FontColor.B:X2}");

                e.Field.Content.End.LoadText(htmlStyle + html, LoadOptions.HtmlDefault);
            }
            // ...
        };
        // ...
    }
}
Imports GemBox.Document

Module Program

    Sub Main()

        ' ...
        AddHandler document.MailMerge.FieldMerging,
            Sub(sender, e)
                ' ...
                If e.FieldName.StartsWith("Html:") Then

                    Dim format As CharacterFormat = e.Field.CharacterFormat

                    ' Use merge field's formatting as default CSS.
                    Dim html As String = CStr(e.Value)
                    Dim htmlStyle As String = String.Format("<style>* {{ font-family:{0}; font-size:{1}pt; font-weight:{2}; font-style:{3}; color:{4}; }}</style>",
                        format.FontName,
                        format.Size,
                        If(format.Bold, "bold", "normal"),
                        If(format.Italic, "italic", "normal"),
                        $"#{format.FontColor.R:X2}{format.FontColor.G:X2}{format.FontColor.B:X2}")

                    e.Field.Content.End.LoadText(htmlStyle & html, LoadOptions.HtmlDefault)
                End If
                ' ...
            End Sub
        ' ...

    End Sub
End Module

Want more?

Next example GitHub

Check the next example or select an example from the menu. You can also download our examples from the GitHub.


Like it?

Download Buy

If you want to try the GemBox.Document yourself, you can download the free version. It delivers the same performance and set of features as the professional version, but with some operations limited. To remove the limitation, you need to purchase a license.