Search code examples
reportmailmergegembox-document

Gembox document range inserting columns instead of rows


I am generating documents by merging a .docx file with a .NET object using GemBox Document. I have one special case where I need to include a table with details of a collection of child objects. However, instead of inserting a row for each object, I need to insert a column. So the "header" cells would be in the leftmost column of the table and each object in the range will have a column added.

A normal table with rows for each object is simple, just do { MERGEFIELD RangeStart:(array) } in the first column and { MERGEFIELD RangeEnd:(array) } in the last column. Is there any way to do this for columns instead of rows though?


Solution

  • Currently, there is no such feature in the mail merge process, but what you could do is have the merge range on the whole table and then combine the resulting tables into one after the mail merge execution.

    For example, let's say this is your document:

    Merge range with table

    I've also set the Title property on this table to "Details" (Table Properties -> Alt Text -> Title) so that I can easily select them.

    var document = DocumentModel.Load("template.docx");
    
    // Sample source.
    var source = new
    {
        Details = new[]
        {
            new { Detail1 = "First", Detail2 = "1", Detail3 = "11", Detail4 = "111" },
            new { Detail1 = "Second", Detail2 = "2", Detail3 = "22", Detail4 = "222" },
            new { Detail1 = "Third", Detail2 = "3", Detail3 = "33", Detail4 = "333" },
        }
    };
    
    document.MailMerge.Execute(source);
    
    // Retrieve tables with "Details" title.
    var detailsTables = document.GetChildElements(true, ElementType.Table)
        .Cast<Table>()
        .Where(t => t.Metadata.Title == "Details")
        .ToList();
    
    // Copy cells from other "Details" tables into the first "Details" table.
    var firstTable = detailsTables[0];
    for (int i = 1; i < detailsTables.Count; i++)
    {
        var otherTable = detailsTables[i];
    
        for (int r = 0; r < otherTable.Rows.Count; r++)
            firstTable.Rows[r].Cells.Add(
                otherTable.Rows[r].Cells[0].Clone(true));
    
        otherTable.Content.Delete();
    }
    
    document.Save("output.docx");
    

    The result is this:

    Merged multiple tables into one table

    Also, instead of using a Title property to identify the targeted tables, you could use a bookmark around the merged range and then retrieve the tables with the following:

    var detailsTables = bookmark.GetContent(true).GetChildElements(ElementType.Table)
        .Cast<Table>();
    

    Last, if you need something like a header column, you can add it as a separate table before the merge range. In other words, something like this:

    Merge range with table that has column header