Table alignment issue when "empty rows" message needs to be shown as a single row

We are using Aspose.Word for Net to generate fresh word DOCX file with rows from API/DB… When we generate the rows we use a function for Header cells, a function for content cell and anothe function to denote empty row message. The table looks aligned when there are rows present. But when there are no rows, the table looks unaligned(please see attached picture). .Even though I match the cellwidths to be same between the Content rows and the “Empty” row, the descrepancy exists.

AddAndConfigureHeaderCell(row, context, "Address Type", 50, ref cellToCloneFrom);
        AddAndConfigureHeaderCell(row, context, "Address", 80, ref cellToCloneFrom);
        AddAndConfigureHeaderCell(row, context, "City, State", 50, ref cellToCloneFrom);
        AddAndConfigureHeaderCell(row, context, "Zip Code", 40, ref cellToCloneFrom);


        Row cRow = null;
        Cell cellToCloneFrom2 = null;
        if (plant.HasOtherAddresses)
        {
            foreach (var address in addresses)
            {
                // create row
                cRow = new Row(doc);
                cRow.RowFormat.AllowBreakAcrossPages = false;
                table.AppendChild(cRow);

                string addressLinesText = address.AddressLine1;
                if (!string.IsNullOrWhiteSpace(address.AddressLine2))
                {
                    addressLinesText += ControlChar.LineBreak + address.AddressLine2;
                }
                string cityState = string.Join(", ", new List<string> { address.City, FsisCommonLookup.USStates.FindByKey(address.UsStateId.Value).AlphaCode2 });

                string addressTypeS = (from a in FsisCommonLookup.AddressTypes.AsSelectListItems()
                                       where (AddressType)(Convert.ToInt32(a.Value)) == address.AddressType
                                      select new { a.Text }).Single().Text;

                AddAndConfigureContentCell(cRow, context, addressTypeS, 50, ref cellToCloneFrom2);
                AddAndConfigureContentCell(cRow, context, addressLinesText, 80, ref cellToCloneFrom2);
                AddAndConfigureContentCell(cRow, context, cityState, 50, ref cellToCloneFrom2);
                AddAndConfigureContentCell(cRow, context, address.PostalCode, 40, ref cellToCloneFrom2);
            }
        }
        else
        {
            AddAndConfigureEmptyRow(table, context, 220, ref cellToCloneFrom2);
        }
         table.PreferredWidth = PreferredWidth.FromPercent(100);
        table.RelativeHorizontalAlignment = HorizontalAlignment.Center;

 private static void AddAndConfigureEmptyRow(Table table, ReportGeneratorContext context, int cellWidth, ref Cell cellToCloneFrom, string cellContent = "No records were found.")
    {
        var doc = (Document)table.Document;
        var row = new Row(doc);
        Paragraph para = new Paragraph(doc);
        //para.ParagraphFormat.KeepWithNext = true;
        row.RowFormat.AllowBreakAcrossPages = false;

        table.AppendChild(row);
        if (cellToCloneFrom == null)
        {
            cellToCloneFrom = new Cell(doc);

            cellToCloneFrom.CellFormat.Width = cellWidth;
            // Add a paragraph to the cell as well as a new run with some text.
            cellToCloneFrom.AppendChild(para);
            cellToCloneFrom.FirstParagraph.AppendChild(new Run(doc, cellContent));

            //Formatting
            cellToCloneFrom.FirstParagraph.Runs[0].Font.Color = context.ContentCellFormat.FontColor;
            cellToCloneFrom.FirstParagraph.Runs[0].Font.Name = context.ContentCellFormat.FontName;
            cellToCloneFrom.FirstParagraph.Runs[0].Font.Size = context.ContentCellFormat.FontSize;
            cellToCloneFrom.FirstParagraph.Runs[0].Font.Italic = true;

            if (context.ContentCellFormat.BackgroundColor.HasValue)
                cellToCloneFrom.CellFormat.Shading.BackgroundPatternColor = context.ContentCellFormat.BackgroundColor.Value;

            cellToCloneFrom.CellFormat.SetPaddings(context.ContentCellFormat.PaddingLeft, context.ContentCellFormat.PaddingTop, context.ContentCellFormat.PaddingRight, context.ContentCellFormat.PaddingBottom);
            // Add the cell to the row.
            row.AppendChild(cellToCloneFrom);
        }
        else
        {
            row.AppendChild(cellToCloneFrom.Clone(false));
            row.LastCell.AppendChild(para);
            row.LastCell.FirstParagraph.AppendChild(new Run(doc, cellContent));

            row.LastCell.FirstParagraph.Runs[0].Font.Color = context.ContentCellFormat.FontColor;
            row.LastCell.FirstParagraph.Runs[0].Font.Name = context.ContentCellFormat.FontName;
            row.LastCell.FirstParagraph.Runs[0].Font.Size = context.ContentCellFormat.FontSize;
            row.LastCell.FirstParagraph.Runs[0].Font.Italic = true;
        }

    }

    /// <summary>
    /// Add a new content cell to the given content row and configure it
    /// </summary>
    /// <param name="row"></param>
    /// <param name="context"></param>
    /// <param name="cellContent"></param>
    /// <param name="cellWidth"></param>
    /// <param name="cellToCloneFrom"></param>
    private static void AddAndConfigureContentCell(Row row, ReportGeneratorContext context, string cellContent, int cellWidth, ref Cell cellToCloneFrom)
    {
        Document doc = (Document)row.Document;
        Paragraph para = new Paragraph(doc);
        if (cellToCloneFrom == null)
        {
            cellToCloneFrom = new Cell(doc);

            cellToCloneFrom.CellFormat.Width = cellWidth;
            // Add a paragraph to the cell as well as a new run with some text.
            
            cellToCloneFrom.AppendChild(para);
            cellToCloneFrom.FirstParagraph.AppendChild(new Run(doc, cellContent));

            //Formatting
            cellToCloneFrom.FirstParagraph.Runs[0].Font.Color = context.ContentCellFormat.FontColor;
            cellToCloneFrom.FirstParagraph.Runs[0].Font.Name = context.ContentCellFormat.FontName;
            cellToCloneFrom.FirstParagraph.Runs[0].Font.Size = context.ContentCellFormat.FontSize;

            if (context.ContentCellFormat.BackgroundColor.HasValue)
                cellToCloneFrom.CellFormat.Shading.BackgroundPatternColor = context.ContentCellFormat.BackgroundColor.Value;

            cellToCloneFrom.CellFormat.SetPaddings(context.ContentCellFormat.PaddingLeft, context.ContentCellFormat.PaddingTop, context.ContentCellFormat.PaddingRight, context.ContentCellFormat.PaddingBottom);
            // Add the cell to the row.
            row.AppendChild(cellToCloneFrom);
        }
        else
        {
            row.AppendChild(cellToCloneFrom.Clone(false));
            row.LastCell.AppendChild(para);
            row.LastCell.FirstParagraph.AppendChild(new Run(doc, cellContent));

            row.LastCell.FirstParagraph.Runs[0].Font.Color = context.ContentCellFormat.FontColor;
            row.LastCell.FirstParagraph.Runs[0].Font.Name = context.ContentCellFormat.FontName;
            row.LastCell.FirstParagraph.Runs[0].Font.Size = context.ContentCellFormat.FontSize;
        }
      
    }

image.png (4.4 KB)

@leopius Using CellFormat.Width property is not recommended. In your case you should use PrefferedWidth:

cellToCloneFrom.CellFormat.PreferredWidth = PreferredWidth.FromPoints(cellWidth);

Also, since you set fixed cell width, you should use FixedColumnWidths autofit behavior:

table.AutoFit(AutoFitBehavior.FixedColumnWidths);

Please see our documentation to learn more about specifying table and cell width.

Also, it would be great if you create a simple console application that will allow us to reproduce your problem, since your code rely on classes that does not exist on our side. This will allow us to work exactly with your scenario.

Thank you very much for your support and suggestion. Will try out and let you know

1 Like

I was not able to get the desired results with your suggestions. Please see our console application and resulting screenshot of the Word DOCX file
image.png (29.1 KB)
ConsoleApp2.zip (16.4 KB)

@leopius Thank you for additional information. first of all the following code does not work as expected, because AddAndConfigureHeaderCell method will clone cellToCloneFrom so all the inserted cells will have the same width, just like the first one:

AddAndConfigureHeaderCell(row, context, "Log Number", 80, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Issue Date", 80, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Regulations", 80, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Status", 60, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Summary", 80, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Is Waiver?", 60, ref cellToCloneFrom);

Also, after building your table, you set table proffered width to 100%, but since you set fixed cell widths, this does not work as expected, so instead of this:

table.PreferredWidth = PreferredWidth.FromPercent(100);

you should use this:

table.AllowAutoFit = false;
table.AutoFit(AutoFitBehavior.FixedColumnWidths);

To make width of the table to fit the window, you can calculate the table width using code like the following:

PageSetup ps = doc.FirstSection.PageSetup;
double tableWidth = ps.PageWidth - ps.LeftMargin - ps.RightMargin;

Then you can calculate width of each particular cell:

// In this case all cells are of the same width, if cells should be of different width, you can 
// use other method for calculation, like tableWidth*0.2 to have 20% width.
double cellWidth = tableWidth / 6; 
AddAndConfigureHeaderCell(row, context, "Log Number", cellWidth, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Issue Date", cellWidth, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Regulations", cellWidth, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Status", cellWidth, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Summary", cellWidth, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Is Waiver?", cellWidth, ref cellToCloneFrom);

then empty row can be inserted like this:

AddAndConfigureEmptyRow(table, context, tableWidth, ref cellToCloneFrom2);

And finally, in your code you make the table floating by specifying RelativeHorizontalAlignment, instead, you should use this to make the table centered:

table.Alignment = TableAlignment.Center;

I have use the following code as per you suggestion, and i got a table which just does out of the page width.
Please see the attached Console application

ConsoleApp2.zip (16.3 KB)

table.AllowAutoFit = false;
table.AutoFit(AutoFitBehavior.FixedColumnWidths);

PageSetup ps = doc.FirstSection.PageSetup;
int tableWidth = Convert.ToInt32(ps.PageWidth - ps.LeftMargin - ps.RightMargin);

Cell cellToCloneFrom = null;
/// create cell
AddAndConfigureHeaderCell(row, context, "Application / Type", tableWidth / 5, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Last Update", tableWidth / 5, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Signed by", tableWidth / 5, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Inspection Type", tableWidth / 5, ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Comments", tableWidth / 5, ref cellToCloneFrom);

Cell cellToCloneFrom2 = null;
//Add content rows if present



AddAndConfigureEmptyRow(table, context, tableWidth, ref cellToCloneFrom2);

Would be great if you could show me just this table aligned in the function RenderApplications

@leopius Sure, here is the modified method:

private static void RenderApplications(DocumentBuilder builder, Document doc, ReportGeneratorContext context)
{
    SetNewParaWithText(doc.FirstSection, "", 10, true);
    SetNewParaWithText(doc.FirstSection, "Applications", 16, true);

    // Create table
    Table table = new Table(doc);
    doc.FirstSection.Body.AppendChild(table);

    PageSetup ps = doc.FirstSection.PageSetup;
    double tableWidth = ps.PageWidth - ps.LeftMargin - ps.RightMargin;
    double cellWidth = tableWidth / 5;

    // Add header
    // create header row
    Row row = new Row(doc);
    row.RowFormat.AllowBreakAcrossPages = false;
    table.AppendChild(row);

    Cell cellToCloneFrom = null;
    /// create cell
    AddAndConfigureHeaderCell(row, context, "Application / Type", cellWidth, ref cellToCloneFrom);
    AddAndConfigureHeaderCell(row, context, "Last Update", cellWidth, ref cellToCloneFrom);
    AddAndConfigureHeaderCell(row, context, "Signed by", cellWidth, ref cellToCloneFrom);
    AddAndConfigureHeaderCell(row, context, "Inspection Type", cellWidth, ref cellToCloneFrom);
    AddAndConfigureHeaderCell(row, context, "Comments", cellWidth, ref cellToCloneFrom);

    Cell cellToCloneFrom2 = null;
    //Add content rows if present

    AddAndConfigureEmptyRow(table, context, tableWidth, ref cellToCloneFrom2);

    // Configure table
    table.AllowAutoFit = false;
    table.AutoFit(AutoFitBehavior.FixedColumnWidths);
    table.Alignment = TableAlignment.Center;

    builder.MoveTo(doc.FirstSection.Body.LastParagraph);
}

Also, I have modified parameter type of cellWidth in AddAndConfigureHeaderCell, AddAndConfigureContentCell and AddAndConfigureEmptyRow methods from int to double.

Thank you very much for the update. Looks like my sage of “int” for width was also very prime reason for my under-formatting. Thank you very much for pointing that out. All you solutions were very useful

One more mystery I was not able to resolve. If you see there is an empty page coming out of this. can you give use us a solution for this. The Testing team has been requesting this for a very long time and I was I was unable to find why I have this empty page towards the end, It would be great if you could help me with remiving this empty page and let me know why I have this empty page in the end

Thank you for all your prompt support

@leopius The problem occurs because in UpdateDocumentUsingProfile you insert an empty section at the end of the document:

doc.AppendChild(new Section(doc));

You can remove this line of code, because when you create a new Document it already contains one empty section.

Thank you very much for the update

1 Like

Regarding your solution for empty row mis-alignment, I was able to wirk where cell widths are same, but when I implemented something similar for Columns with differnt widths, I am getting a descrepancey for the column widthd

This is the code I adapted from your solution:

double sumOfColWidthsPlanned = 440.0;

AddAndConfigureHeaderCell(row, context, "Log Number", (80.0 / sumOfColWidthsPlanned) * (tableWidth), ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Issue Date", (80.0 / sumOfColWidthsPlanned) * (tableWidth), ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Regulations", (80.0 / sumOfColWidthsPlanned) * (tableWidth), ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Status", (60.0 / sumOfColWidthsPlanned) * (tableWidth), ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Summary", (80.0 / sumOfColWidthsPlanned) * (tableWidth), ref cellToCloneFrom);
AddAndConfigureHeaderCell(row, context, "Is Waiver?", (60.0 / sumOfColWidthsPlanned) * (tableWidth), ref cellToCloneFrom);

Cell cellToCloneFrom2 = null;
Row cRow = null;
AddAndConfigureEmptyRow(table, context, (tableWidth), ref cellToCloneFrom2);

image.png (8.6 KB)
ConsoleApp2.zip (16.4 KB)

@leopius The problem occurs because you use ref. So when you do the first call:

Cell cellToCloneFrom = null;
/// create cell
double sumOfColWidthsPlanned = 440.0;

AddAndConfigureHeaderCell(row, context, "Log Number", (80.0 / sumOfColWidthsPlanned) * (tableWidth), ref cellToCloneFrom);

cellToCloneFrom is null and cell is created from scratch and it’s width is set, but in the subsequent calls of AddAndConfigureHeaderCell the cellToCloneFrom is initialized with the Cell created by the previous call. So you should either avoid using ref parameters or modify your AddAndConfigureHeaderCell routine to set cell width even if cellToCloneFrom is specified.
Change this:

row.AppendChild(cellToCloneFrom.Clone(false));

with this:

Cell cell = (Cell)cellToCloneFrom.Clone(false);
cell.CellFormat.PreferredWidth = PreferredWidth.FromPoints(cellWidth);
row.AppendChild(cell);

Thank you for the solution Alexey… It was so prompt !!

1 Like

Connected to the previous suggestions made, we had issue that came back, saying the widths were were not as programmed for.
Please note that we have set widths as per your specifications and some case, it is not prodicing expected results;
Attached the code and and resulting output

ConsoleApp2.zip (115.7 KB)

@leopius You should add the following lines at the end of the RenderDoingBusinessAs method:

// Configure table
table.AllowAutoFit = false;
table.AutoFit(AutoFitBehavior.FixedColumnWidths);
table.Alignment = TableAlignment.Center;

In this case the table is generated properly. Also, I see you sometimes use

table.PreferredWidth = PreferredWidth.FromPercent(100);
table.RelativeHorizontalAlignment = HorizontalAlignment.Center;

Since you set fixed preferred widths of cells, you should replace these lines with the lines I have mentioned above.