TX Text Control .NET for WPF

Tables and Calculations

Topic

A table in a TX Text Control document can calculate: a total under a column of prices updates itself when a price changes, much like a cell in a spreadsheet. Before that, your code creates the table, fills it from data, formats it and changes its structure. Each task is a Click handler for the TextControl of a WPF window, including work on the table the user is in.

A first table with a header row is part of Create a Document Programmatically. How users insert and format tables with the ribbon is covered in Tables and Table Formulas, and a table row that repeats for each record of a data source is a merge block, described in Merge Blocks. MainWindow.xaml.cs needs these using directives:

using System;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Windows;
using TXTextControl;

How Tables Are Organized

A Table has rows, columns and cells numbered from 1, your code finds it by its ID or at the input position, and its widths and borders are twips, as How Tables Are Organized in Tables and Table Formulas describes. In processing code, one more rule matters: Tables of the TextControl returns the TableCollection of the text part with the input focus, which is a header, footer or text frame while the user edits one. Document Object Model describes the rule. Table widths and borders don't depend on the PageUnit of the control.

Fill a Table From Data

The following handler creates the line item table of an invoice, with a gray header row that is repeated on each page:

private void InvoiceTableButton_Click(object sender, RoutedEventArgs e)
{
    var lineItems = new[]
    {
        new { Description = "TX Text Control .NET for WPF License", Quantity = 1, Price = 1999.00 },
        new { Description = "Priority Support - 12 Months", Quantity = 1, Price = 299.00 }
    };

    textControl1.ResetContents();
    textControl1.Selection.Text = "Invoice INV-2025-001\r\n\r\n";

    // A header row and one row for each item, with the ID 10
    textControl1.Tables.Add(lineItems.Length + 1, 3, 10);
    TXTextControl.Table table = textControl1.Tables.GetItem(10);

    // One format for all cells: borders of 1 pt and space left and right of the text
    TableCellFormat cellFormat = new TableCellFormat()
    {
        LeftBorderWidth = 20,
        TopBorderWidth = 20,
        RightBorderWidth = 20,
        BottomBorderWidth = 20,
        LeftTextDistance = 100,
        RightTextDistance = 100
    };

    foreach (TXTextControl.TableCell cell in table.Cells)
    {
        cell.CellFormat = cellFormat;
    }

    // Column headings in row 1, repeated on every page
    string[] headings = { "Description", "Quantity", "Price" };

    for (int column = 1; column <= headings.Length; column++)
    {
        table.Cells.GetItem(1, column).Text = headings[column - 1];
    }

    TXTextControl.TableRow headerRow = table.Rows.GetItem(1);
    headerRow.IsHeader = true;
    headerRow.CellFormat = new TableCellFormat() { BackColor = System.Drawing.Color.LightGray };

    // One row for each item, starting in row 2
    for (int i = 0; i < lineItems.Length; i++)
    {
        int row = i + 2;
        table.Cells.GetItem(row, 1).Text = lineItems[i].Description;
        table.Cells.GetItem(row, 2).Text = lineItems[i].Quantity.ToString(CultureInfo.InvariantCulture);
        table.Cells.GetItem(row, 3).Text = lineItems[i].Price.ToString("0.00", CultureInfo.InvariantCulture);
    }

    // The first column is 3.5 inches wide
    table.Columns.GetItem(1).Width = (int)(3.5 * 1440);
}

The code works as follows:

Format Cells, Rows and Columns

A TableCellFormat has these properties:

PropertySets
BackColorThe background color, as a System.Drawing.Color
LeftBorderWidth, TopBorderWidth, RightBorderWidth, BottomBorderWidthThe width of each border in twips
LeftBorderColor, TopBorderColor, RightBorderColor, BottomBorderColorThe color of each border
LeftTextDistance, TopTextDistance, RightTextDistance, BottomTextDistanceThe distance between the border and the text in twips
VerticalAlignmentTop, Center or Bottom
TextType, NumberFormatWhether the text of the cell is a number, and how numbers are displayed

Inside a window, VerticalAlignment alone means the layout property of the window. Write the value with its namespace, as in VerticalAlignment = TXTextControl.VerticalAlignment.Center.

TableRow.MinimumHeight is the smallest height of a row, which grows with its text. TableRow.AllowPageBreak set to false keeps a row on one page. To format the text in a cell, select it with TableCell.Select, then use the selection:

TXTextControl.Table table = textControl1.Tables.GetItem(10);

// Bold text in the first cell
table.Cells.GetItem(1, 1).Select();
textControl1.Selection.Bold = true;

Work With the Table at the Input Position

Commands such as Insert Row Below act on the table the user is in: Tables.GetItem() without a parameter returns it, Rows.Add and Columns.Add insert rows and columns next to the input position, and Table.Select with MergeCells merges cells. Work With the Table at the Input Position in Tables and Table Formulas shows the code, with buttons that are enabled only while the input position is in a table. TableCell.Row and TableCell.Column of Cells.GetItem() tell where in the table the input position is.

InputPosition moves the input position into a cell from code, for example before your code adds a row there. TableCell.Start counts from 1, and the InputPosition constructor expects a text position that counts from 0, as the next example shows.

Calculate With Formulas

Formulas in table cells follow the syntax of Office Open XML spreadsheet formulas. Calculate With Formulas in Tables and Table Formulas explains how to switch the calculation on, choose the A1 or R1C1 reference style, make cells numbers with a number format, set a formula and catch the FormulaException of an invalid one, and it describes constants, operators, functions and cell references with a result for each example. Number Formats on the same page gives the rules for number formats, and Formula Functions describes each of the 85 functions.

The following handler adds a total row to the invoice table of the first example. It moves the input position into the last row before adding a row, so it works whatever the user clicked last:

private void TotalButton_Click(object sender, RoutedEventArgs e)
{
    textControl1.FormulaReferenceStyle = FormulaReferenceStyle.A1;
    textControl1.IsFormulaCalculationEnabled = true;

    TXTextControl.Table table = textControl1.Tables.GetItem(10);

    if (table == null)
    {
        return;
    }

    // Put the input position into the last row and add a row below it
    TXTextControl.TableCell lastCell = table.Cells.GetItem(table.Rows.Count, 1);
    textControl1.InputPosition = new InputPosition(lastCell.Start - 1);
    table.Rows.Add(TableAddPosition.After, 1);

    int totalRow = table.Rows.Count;
    table.Cells.GetItem(totalRow, 1).Text = "Total";

    TableCellFormat amountFormat = new TableCellFormat()
    {
        TextType = TextType.Number,
        NumberFormat = "#,##0.00"
    };

    for (int row = 2; row <= totalRow; row++)
    {
        table.Cells.GetItem(row, 3).CellFormat = amountFormat;
    }

    // The sum of the price column, from row 2 to the row above the total
    table.Cells.GetItem(totalRow, 3).Formula = $"SUM(C2:C{totalRow - 1})";
}

Formulas can make a whole report calculate itself. In the following cash flow table from Text Control's spreadsheet sample, the green rows are formulas that add up the rows above them:

A cash flow table with yearly columns in which the green subtotal rows are calculated from the rows above them by table formulas.

The how-to sample Howto\Spreadsheet, installed with TX Text Control .NET for WPF, builds a formula bar in which the user selects a function from the function list and clicks Add function:

The function list of the Spreadsheet how-to sample, here in its Windows Forms version, opened over the Cash Flow Report with SUM selected among functions from ODD to TYPE.

The sample then asks for the cell range of the formula:

The formula box of the Spreadsheet how-to sample, here in its Windows Forms version, with SUM() and a Next Step tip asking for the cell range, for example SUM(A1:A5).

Text Control's feature comparison lists Excel formulas for the Enterprise and Professional editions.

Nest Tables

A table goes into a cell of another table when the input position is in the cell, with no text selected, and Tables.Add is called:

private void NestButton_Click(object sender, RoutedEventArgs e)
{
    TXTextControl.Table outerTable = textControl1.Tables.GetItem(10);

    // Put the input position into cell (1, 1) without selecting text
    outerTable.Cells.GetItem(1, 1).Select();
    textControl1.Selection.Length = 0;

    textControl1.Tables.Add(2, 2, 11);
    TXTextControl.Table nestedTable = outerTable.NestedTables.GetItem(11);

    MessageBox.Show($"Nesting level {nestedTable.NestedLevel}, outer table {nestedTable.OuterTable.ID}");
}

Table.NestedTables holds the tables inside a table. NestedLevel is 1 for a table at the top level, and OuterTable is the table around a nested one.

Read and Remove Tables

A loaded document's tables are in the same collections. The following handler lists the tables of the main text and the text of their cells:

private void ReadTablesButton_Click(object sender, RoutedEventArgs e)
{
    StringBuilder report = new StringBuilder();
    MainText mainText = textControl1.TextParts.GetMainText();

    foreach (TXTextControl.Table table in mainText.Tables)
    {
        report.AppendLine($"Table {table.ID}: {table.Rows.Count} rows, {table.Columns.Count} columns");

        foreach (TXTextControl.TableCell cell in table.Cells)
        {
            report.AppendLine($"  ({cell.Row}, {cell.Column}): {cell.Text}");
        }
    }

    MessageBox.Show(report.ToString());
}

TableBaseCollection.Remove removes the table with an ID, as in textControl1.Tables.Remove(10). Without a parameter, TableCollection.Remove removes the table at the input position, and Clear removes all tables of the collection.

Next Steps