@M.Heinz,
We can think of two possible solutions to this scenario:
-
With template modification:
The template document can be modified in the way when each table with row mailmerge region is wrapped by IF field with following code: { IF { MERGEFIELD Table-<REGION_NAME>-COUNT` } <> 0 … } where REGION_NAME is the row mailmerge region name. So the nested table is only present in result if IF field condition is met, i.e. mailmerge region has records. The corresponding MERGEFIELD values can be added to the dataset in runtime:
public void TestIF()
{
var document = new Document("template.IF.docx");
var dataSet = GetSampleDataSet();
var topLevelFields = EnhanceDataSet(dataSet, document);
document.MailMerge.CleanupOptions =
MailMergeCleanupOptions.RemoveEmptyTableRows |
MailMergeCleanupOptions.RemoveContainingFields |
MailMergeCleanupOptions.RemoveStaticFields |
MailMergeCleanupOptions.RemoveUnusedFields |
MailMergeCleanupOptions.RemoveEmptyParagraphs;
document.MailMerge.ExecuteWithRegions(dataSet);
document.MailMerge.Execute(topLevelFields.fields, topLevelFields.values);
document.Save("out.IF.docx");
}
private static DataSet GetSampleDataSet()
{
var dataSet = new DataSet();
dataSet.ReadXml("ds.xml");
return dataSet;
}
private static (string[] fields, object[] values) EnhanceDataSet(DataSet dataset, Document document)
{
foreach (DataTable table in dataset.Tables)
{
var fieldName = $"Table-{table.TableName}-Count";
foreach (DataRelation relation in table.ParentRelations)
{
var parentTable = relation.ParentTable;
var column = parentTable.Columns.Add(fieldName, typeof(int));
foreach (DataRow parentRow in parentTable.Rows)
parentRow[column] = parentRow.GetChildRows(relation).Length;
}
}
var topmostRegions = document.MailMerge.GetRegionsHierarchy().Regions.Select(p => p.Name).ToList();
return (
topmostRegions.Select(p => $"Table-{p}-Count").ToArray(),
topmostRegions.Select(p => dataset.Tables[p]?.Rows.Count ?? 0).Cast<object>().ToArray()
);
}
- Code only:
NOTE: This solution requires WORDSNET-22717 to be integrated in next 21.10 version of Aspose.Words.
This solution supports tables with multiple row mailmerge regions in the same table.
public void Test()
{
var document = new Document("template.docx");
MarkupTables(document.MailMerge.GetRegionsHierarchy().Regions);
var dataSet = GetSampleDataSet();
document.MailMerge.CleanupOptions =
MailMergeCleanupOptions.RemoveEmptyTableRows |
MailMergeCleanupOptions.RemoveContainingFields |
MailMergeCleanupOptions.RemoveStaticFields |
MailMergeCleanupOptions.RemoveUnusedFields |
MailMergeCleanupOptions.RemoveEmptyParagraphs |
MailMergeCleanupOptions.RemoveUnusedRegions;
document.MailMerge.ExecuteWithRegions(dataSet);
CleanupTables(document);
document.Save("out.docx");
}
private static DataSet GetSampleDataSet()
{
var dataSet = new DataSet();
dataSet.ReadXml("ds.xml");
return dataSet;
}
private static void MarkupTables(IEnumerable<MailMergeRegionInfo> regions)
{
foreach (var region in regions)
{
MarkupTables(region);
MarkupTables(region.Regions);
}
}
private static void MarkupTables(MailMergeRegionInfo region)
{
var startCell = region.StartField.Start.ParentParagraph.ParentNode as Cell;
if (startCell == null)
return;
var endCell = region.EndField.End.ParentParagraph.ParentNode as Cell;
if (endCell == null)
return;
if (startCell.ParentRow != endCell.ParentRow)
return;
var regionRow = startCell.ParentRow;
var headerRow = regionRow.ParentTable.FirstRow;
if (headerRow == regionRow)
return;
InsertSmartTag(regionRow, TableRegionRowSmartTagProperty).Value = region.Name;
InsertSmartTag(headerRow, TableHeaderRowSmartTagProperty).Value = region.Name;
}
private static CustomXmlProperty InsertSmartTag(Row row, string key)
{
var tag = new SmartTag(row.Document);
row.FirstCell.FirstParagraph.InsertAfter(tag, null);
var property = new CustomXmlProperty(key, string.Empty, string.Empty);
tag.Properties.Add(property);
return property;
}
private static void CleanupTables(Document document)
{
foreach (Table table in document.GetChildNodes(NodeType.Table, true))
{
var regionNames = GetRowSmartTags(table.FirstRow, TableHeaderRowSmartTagProperty).ToList();
if (!regionNames.Any())
continue;
if (regionNames.Any(p => IsRegionRowSmartTagPresence(table, p)))
continue;
table.Remove();
}
document.RemoveSmartTags();
}
private static bool IsRegionRowSmartTagPresence(Table table, string regionName)
{
foreach (Row row in table.Rows.Skip(1))
{
if (GetRowSmartTags(row, TableRegionRowSmartTagProperty).Any(p => p == regionName))
return true;
}
return false;
}
private static IEnumerable<string> GetRowSmartTags(Row row, string key)
{
return row.FirstCell.FirstParagraph?.GetChildNodes(NodeType.SmartTag, false)
.Cast<SmartTag>()
.Select(p => p.Properties[key])
.Where(p => p != null)
.Select(p => p.Value) ?? Enumerable.Empty<string>();
}
private const string TableRegionRowSmartTagProperty = "row-id";
private const string TableHeaderRowSmartTagProperty = "header-id";