How to conditionally hide or remove table rows during a mail merge in GemBox.Document based on dynamic conditions from the template?

Hello everyone,

I’m currently working on a C# project that uses GemBox.Document to perform a mail merge operation. I have a Word template that contains a table with merge fields. I’m using a JSON object as the data source for the mail merge. The JSON object contains a list of officers, each with an “InclExcl” property, which can be either “include” or “exclude.”

My goal is to conditionally hide or remove table rows during the mail merge process based on dynamic conditions coming from fields in the template itself. The conditions are not hardcoded in the C# code and should be evaluated within the template. For example, if the “InclExcl” value is “exclude”, I want to hide or remove the entire table row for that officer. I cannot modify the JSON object.

I tried using the FieldMerging event, but it seems to process all rows, and I couldn’t find a way to hide or remove the table row based on the “InclExcl” value.

Could someone please suggest a solution or alternative approach to achieve this functionality using GemBox.Document, considering the dynamic nature of the conditions coming from the template itself?

Thank you!

Hi Noe,

How about this, let’s say you have a template document with “MyRange” merge range:

Then in the FieldMerging event, you could check that the data record for the “MyRange” doesn’t have an “exclude” value for the InclExcl property.

If it does, you can cancel the merging by setting the e.Inline to null. Also, by using the MailMergeClearOptions.RemoveEmptyTableRows the row will be removed because all the fields inside it were canceled.

In other words, something like this:

var source = new[]
{
    new { InclExcl = "include", Name="Person 1", Company = "Company 1"},
    new { InclExcl = "exclude", Name="Person 2", Company = "Company 2"},
    new { InclExcl = "exclude", Name="Person 3", Company = "Company 3"},
    new { InclExcl = "include", Name="Person 4", Company = "Company 4"},
    new { InclExcl = "include", Name="Person 5", Company = "Company 5"},
    new { InclExcl = "exclude", Name="Person 6", Company = "Company 6"},
};

var document = DocumentModel.Load("input.docx");

document.MailMerge.FieldMerging += (sender, e) =>
{
    if (!e.IsValueFound || e.Value == null)
        return;

    if (e.MergeContext.RangeName == "MyRange")
    {
        dynamic record = e.MergeContext.Record;
        if (record.InclExcl == "exclude")
            e.Inline = null;
    }
};

document.MailMerge.ClearOptions = MailMergeClearOptions.RemoveEmptyTableRows;
document.MailMerge.Execute(source, "MyRange");

document.Save("output.docx");

Last, here is the output file:

I hope this helps.

Regards,
Mario

Thank you for your quick response. The problem with this solution is somehting that I might not state it clearly but I am going to try to restate it:

Conditions are in templates, meaning I can’t code them. I think the only way to do that is using IF fields in combination with COMBINE (I don’t remember the name exactly). Then, when merging, I need to read result and filter out rows didn’t pass validation.

Noe, there is currently no built-in function for this so you’ll need to define some logic that you’ll either handle in the MailMerge.FieldMerging event or after the MailMerge.Execute method.

So, for example, you could have an IF field that would add some custom text which would be an indicator for you to delete that parent row.
In other words, let’s say you have this conditional field:

{ IF { MERGEFIELD InclExcl = "exclude" "%DELETE-ROW%" "" } }

Now after the mail merge process, you could check what rows have “%DELETE-ROW%” text and delete them. Like this:

var document = DocumentModel.Load("input.docx");
document.MailMerge.Execute(source, "MyRange");

document.GetChildElements(true, ElementType.TableRow)
    .Where(r => r.Content.ToString().Contains("%DELETE-ROW%"))
    .ToList()
    .ForEach(r => r.Content.Delete());

document.Save("output.docx");

Would this work for you?

Regards,
Mario

Yup… thanks, this solves the issue

I would like to ask your opinion about this solution I tried by following your same idea… is it the same? is it worse?

var document = DocumentModel.Load("input.docx");
document.MailMerge.ClearOptions = MailMergeClearOptions.RemoveEmptyTableRows; 
document.MailMerge.Execute(source);
foreach (ContentRange rowContent in document.Content.Find("_delrow").Reverse())
  {
      Element? parent = rowContent.Start.Parent;
      while (parent != null && !(parent is TableRow))
      {
          parent = parent.Parent;
      }
      if (parent is TableRow row)
      {
          Table? table = row?.Parent as Table;
          if (table != null)
          {
              table.Rows.Remove(row);
          }
    }
}
document.Save("output.docx");

It’s basically the same, it’s just that in my case I’m searching only the content of the TableRow elements while in your case you’re searching the content of the whole document.

Nevertheless, you could simply your iteration by using the Element.GetParentElements method, like this:

foreach (ContentRange rowContent in document.Content.Find("_delrow").Reverse())
{
    Element row = rowContent.Start.Parent.GetParentElements(ElementType.TableRow).FirstOrDefault();
    row?.Content.Delete();
}

I hope this helps.

Regards,
Mario

1 Like