IFieldMergingCallback is not working as expected for multiple merge records

Follow up to this thread: IFieldMergingCallback: how to highlight unmerged field

When doing a simple merge with a single record, as shown in the above thread, it works fine, but when implementing IMailMergeDataSource with multiple records, things are not working as expected. Please see the example below.

What I wanted to achieve: highlight merged fields with yellow, highlight unmerged or empty value merged fields with red.

Example template:

Template.docx

{{FirstName}} {{LastName}}

When executing the test app below, the output looks like this:

first page (correct output):
John
Doe
second page (incorrect output):
Mike
Smith
third page (incorrect output):
«LastName»

The expected output should be:

first page:
John Doe
second page:
«FirstName» Smith
third page:
Mike «LastName»

My thoughts:

I think what’s happening is that on this line:

_documentBuilder.MoveToMergeField(args.FieldName);

it moves to the first occurrence of the FieldName and not the field name of the current record, therefore inserting “Mike” into the second page instead of the third page.

Is there a correct way of implementing IFieldMergingCallback to fix this issue?

Thanks!

Test console app (please see attached zip if you want to run the sample):

class Program
{
    static void Main(string[] args)
    {
        var doc = new Document(@"Template.docx");
        doc.MailMerge.UseNonMergeFields = true;
        doc.MailMerge.FieldMergingCallback = new HighLightFieldMergingCallback();
        doc.MailMerge.Execute(GetDataSource());
        doc.Save(@"Output.docx", SaveFormat.Docx);
    }

    static IMailMergeDataSource GetDataSource()
    {
        var dataDictionary = new List<IDictionary<string, string>>
    {
        new Dictionary<string, string>
        {
            { "FirstName", "John" },
            { "LastName", "Doe" }
        },
        new Dictionary<string, string>
        {
            { "FirstName", "" },
            { "LastName", "Smith" }
        },
        new Dictionary<string, string>
        {
            { "FirstName", "Mike" },
            { "LastName", "" }
        }
    };

        return new MailMergeDataSource(dataDictionary);
    }

}
public class HighLightFieldMergingCallback : IFieldMergingCallback

{

    private DocumentBuilder _documentBuilder;
    public void FieldMerging(FieldMergingArgs args)
    {
        if (_documentBuilder == null)
            _documentBuilder = new DocumentBuilder(args.Document);

        if (!string.IsNullOrWhiteSpace(args.FieldValue as string))
        {
            _documentBuilder.MoveToMergeField(args.FieldName);
            _documentBuilder.Font.HighlightColor = Color.Yellow;
            _documentBuilder.Write(args.FieldValue.ToString());
            args.Text = string.Empty;
        }
        else
        {
            var fieldCode = args.Field.GetFieldCode().Trim();
            _documentBuilder.MoveToMergeField(args.FieldName);
            _documentBuilder.Font.HighlightColor = Color.Salmon;
            _documentBuilder.InsertField(fieldCode);
        }
    }

    public void ImageFieldMerging(ImageFieldMergingArgs args)
    {

    }

}
public class MailMergeDataSource : IMailMergeDataSource

{

    private readonly List<IDictionary<string, string>> _dataSources;

    private int _index;
    public MailMergeDataSource(IEnumerable<IDictionary<string, string>> dataSources)
    {
        _dataSources = dataSources.ToList();
        _index = -1;
    }

    public bool MoveNext()
    {
        _index++;
        return _index < _dataSources.Count;
    }

    public bool GetValue(string fieldName, out object fieldValue)
    {
        var currentRecord = _dataSources[_index];
        try
        {
            fieldValue = currentRecord[fieldName];
            return fieldValue != null;
        }
        catch (KeyNotFoundException)
        {
            fieldValue = null;
            return false;
        }
    }

    public IMailMergeDataSource GetChildDataSource(string tableName)
    {
        return null;
    }

    public string TableName => null;

}

Hi Minh,

Thanks for your inquiry. Please use following code example to achieve your requirements. Hope this helps you. Please let us know if you have any more queries.

static void Main(string[] args)
{
    var doc = new Document(@"Template.docx");
    doc.MailMerge.UseNonMergeFields = true;
    doc.MailMerge.FieldMergingCallback = new HighLightFieldMergingCallback();
    doc.MailMerge.Execute(GetDataSource());
    DocumentBuilder builder = new DocumentBuilder(doc);
    foreach (Bookmark bookmark in doc.Range.Bookmarks)
    {
        if (bookmark.Name.StartsWith("bm_"))
        {
            builder.MoveToBookmark(bookmark.Name);
            builder.Font.HighlightColor = Color.Salmon;
            builder.InsertField(@"MERGEFIELD " + bookmark.Name.Split('_')[1] + @" \* MERGEFORMAT");
        }
    }
    doc.Save(@"Output.docx", SaveFormat.Docx);
}
static IMailMergeDataSource GetDataSource()
{
    var dataDictionary = new List<IDictionary<string, string>>
    {
        new Dictionary<string, string>
        {
            { "FirstName", "John" },
            { "LastName", "Doe" }
            },
        new Dictionary<string, string>
            {
            { "FirstName", "" },
            { "LastName", "Smith" }
            },
        new Dictionary<string, string>
            {
            { "FirstName", "Mike" },
            { "LastName", "" }
            }
        };
        return new MailMergeDataSource(dataDictionary);
    }
}
public class HighLightFieldMergingCallback : IFieldMergingCallback
{
    private DocumentBuilder _documentBuilder;
    private int i = 1;
    public void FieldMerging(FieldMergingArgs args)
    {
        if (_documentBuilder == null)
            _documentBuilder = new DocumentBuilder(args.Document);
        if (!string.IsNullOrWhiteSpace(args.FieldValue as string))
        {
            _documentBuilder.MoveToMergeField(args.FieldName);
            _documentBuilder.Font.HighlightColor = Color.Yellow;
            _documentBuilder.Write(args.FieldValue.ToString());
            args.Text = string.Empty;
        }
        else
        {
            var fieldCode = args.Field.GetFieldCode().Trim();
            _documentBuilder.MoveToMergeField(args.FieldName);
            _documentBuilder.StartBookmark("bm_" + args.FieldName + "_" + i);
            _documentBuilder.EndBookmark("bm_" + args.FieldName + "_" + i);
            i++;
        }
    }
    public void ImageFieldMerging(ImageFieldMergingArgs args)
    {
    }
}