Tuesday, July 24, 2012

Building Tables In FlowDocuments

Recently I've been working on a WPF task management application. I decided that it would be nice to add some reporting capabilities, but wasn't sure about the best reporting option considering that I am using Visual C# 2010 Express and SQL Server CE 3.5.

At work I had already had some exposure to building documents using the FlowDocument class, so I thought that could serve as a decent reporting mechanism for the simple reports that I was trying to make. After completing the first report using a tabular layout, I realized that my table building code would be pretty much similar for all of the reports. As a result, I factored it out into a common method, which you can see below.

public static Table BuildTable(Dictionary columnDefinitions, List rowRecords)
    Table flowTable = new Table();
    flowTable.CellSpacing = 10;
    flowTable.Background = System.Windows.Media.Brushes.White;

    // Create columns and add them to the table's Columns collection.
    int numberOfColumns = columnDefinitions.Count;
    for (int x = 0; x < numberOfColumns; x++)
        flowTable.Columns.Add(new TableColumn());

    // Create and add an empty TableRowGroup to hold the table's Rows.
    flowTable.RowGroups.Add(new TableRowGroup());

    // Add the header row.
    flowTable.RowGroups[0].Rows.Add(new TableRow());
    TableRow currentRow = flowTable.RowGroups[0].Rows[0];

    // Global formatting for the header row.
    currentRow.FontSize = 18;
    currentRow.FontWeight = FontWeights.Bold;

    // Add cells with content to the second row.
    foreach (string propertyName in columnDefinitions.Keys)
        currentRow.Cells.Add(new TableCell(new Paragraph(new Run(columnDefinitions[propertyName]))));

    int currentRowCount = 1;

    // loop through content and output table rows.
    foreach (T rowRecord in rowRecords)
        flowTable.RowGroups[0].Rows.Add(new TableRow());
        currentRow = flowTable.RowGroups[0].Rows[currentRowCount];
        currentRow.FontSize = 12;
        currentRow.FontWeight = FontWeights.Normal;

        foreach (string propertyName in columnDefinitions.Keys)
            object propertyValue = rowRecord.GetType().GetProperty(propertyName).GetValue(rowRecord, null);
            string convertedValue = propertyValue == null ? "" : propertyValue.ToString();
            currentRow.Cells.Add(new TableCell(new Paragraph(new Run(convertedValue))));


    return flowTable;

The first parameter is a dictionary of property names and the column titles that go with them. The second is a list of the row records used to build the table. Below is some sample code for calling the method.

Dictionary columnDefinitions = new Dictionary()
    {"Name", "Name"},
    {"Rank", "Rank"},
    {"SerialNumber", "Serial Number"}

List activePeople = tData.GetActivePeople();

flowDocument.Blocks.Add(FlowDocumentHelper.BuildTable(columnDefinitions, activePeople));