// corner sase: if two cells end up on the same row (a.Row + a.RowSpan = b.Row + b.RowSpan), // bottom edges of their bounding boxes should be at the same level static void AdjustCellSizes(ICollection <TableCellRenderingCommand> commands, DynamicDictionary <int, float> rowBottomOffsets) { foreach (var command in commands) { var lastRow = command.Cell.Row + command.Cell.RowSpan - 1; var height = rowBottomOffsets[lastRow] - command.Offset.Y; command.Size = new Size(command.Size.Width, height); command.Offset = new Position(command.Offset.X, rowBottomOffsets[command.Cell.Row - 1]); } }
ICollection <TableCellRenderingCommand> GetRenderingCommands() { var rowBottomOffsets = new DynamicDictionary <int, float>(); var commands = new List <TableCellRenderingCommand>(); var cellsToTry = Cells.Where(x => x.Row + x.RowSpan - 1 >= CurrentRow); var currentRow = CurrentRow; var maxRenderingRow = RowsCount; foreach (var cell in cellsToTry) { // update position of previous row if (cell.Row > currentRow) { rowBottomOffsets[currentRow] = Math.Max(rowBottomOffsets[currentRow], rowBottomOffsets[currentRow - 1]); if (rowBottomOffsets[currentRow - 1] > availableSpace.Height + Size.Epsilon) { break; } foreach (var row in Enumerable.Range(cell.Row, cell.Row - currentRow)) { rowBottomOffsets[row] = Math.Max(rowBottomOffsets[row - 1], rowBottomOffsets[row]); } currentRow = cell.Row; } // cell visibility optimizations if (cell.Row > maxRenderingRow) { break; } // calculate cell position / size var topOffset = rowBottomOffsets[cell.Row - 1]; var availableWidth = GetCellWidth(cell); var availableHeight = availableSpace.Height - topOffset; var availableCellSize = new Size(availableWidth, availableHeight); var cellSize = cell.Measure(availableCellSize); // corner case: if cell within the row is not fully rendered, do not attempt to render next row if (cellSize.Type == SpacePlanType.PartialRender) { maxRenderingRow = Math.Min(maxRenderingRow, cell.Row + cell.RowSpan - 1); } // corner case: if cell within the row want to wrap to the next page, do not attempt to render this row if (cellSize.Type == SpacePlanType.Wrap) { maxRenderingRow = Math.Min(maxRenderingRow, cell.Row - 1); continue; } // update position of the last row that cell occupies var bottomRow = cell.Row + cell.RowSpan - 1; rowBottomOffsets[bottomRow] = Math.Max(rowBottomOffsets[bottomRow], topOffset + cellSize.Height); // accept cell to be rendered commands.Add(new TableCellRenderingCommand() { Cell = cell, Measurement = cellSize, Size = new Size(availableWidth, cellSize.Height), Offset = new Position(columnOffsets[cell.Column - 1], topOffset) }); } if (!commands.Any()) { return(commands); } var maxRow = commands.Select(x => x.Cell).Max(x => x.Row + x.RowSpan); foreach (var row in Enumerable.Range(CurrentRow, maxRow - CurrentRow)) { rowBottomOffsets[row] = Math.Max(rowBottomOffsets[row - 1], rowBottomOffsets[row]); } AdjustCellSizes(commands, rowBottomOffsets); // corner case: reject cell if other cells within the same row are rejected return(commands.Where(x => x.Cell.Row <= maxRenderingRow).ToList()); }