TX Text Control .NET Server

Form Fields

Topic

An order form, an insurance claim or a checklist becomes fillable when its blanks are form fields: text boxes, check boxes, drop-down lists and date pickers that users fill out in the Document Editor, the Document Viewer, MS Word or a PDF reader. On the server, your code creates such forms, reads and checks the values users entered and replaces the fields with their values before the document is archived or sent.

MS Word forms with content controls are covered in Content Controls, which reads and fills DOCX forms. Filling form fields from data with MailMerge is part of Merge Fields. The examples below are console programs for Program.cs.

Form Field Types

All form fields derive from FormField, which in turn derives from TextField, and are managed in the FormFieldCollection of ServerTextControl.FormFields. There are four types:

ClassForm fieldValueSettings
TextFormFieldText boxTextEmptyWidth, the width in twips while the field is empty
CheckFormFieldCheck boxCheckedCheckedCharacter and UncheckedCharacter, the characters that show the checked and the unchecked state
SelectionFormFieldDrop-down list or combo boxSelectedIndex and TextItems, IsDropDownArrowVisible, EmptyWidth
DateFormFieldDate pickerDate, null when emptyDateFormat, IsDateControlVisible, EmptyWidth

Every form field also has the properties of a text field, such as Name and ID, which identify it, and Deleteable, which decides whether users can delete it. Two properties belong to form fields only:

  • Enabled decides whether users can fill out the field in a protected form. It's true by default.
  • DescriptiveText describes the field for screen readers and in accessible PDF documents.

Create a Fillable Form

A form field is inserted at the current input position. The following program writes a heading, adds a table with a label column and inserts one form field of each type into the second column. It saves the form in three formats:

using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    tx.Selection.Text = "Order Form\r\n\r\n";

    // A table with a label column and a column for the form fields
    tx.Tables.Add(4, 2, 10);
    Table table = tx.Tables.GetItem(10);

    table.Cells.GetItem(1, 1).Text = "Customer";
    table.Cells.GetItem(2, 1).Text = "Product";
    table.Cells.GetItem(3, 1).Text = "Invoice date";
    table.Cells.GetItem(4, 1).Text = "Terms accepted";

    // A text box that is 2 inches (2880 twips) wide while it's empty
    TextFormField customer = new TextFormField(2880)
    {
        Name = "Customer",
        ID = 1,
        Text = "Acme Corporation"
    };

    // A drop-down list with the first item selected
    SelectionFormField product = new SelectionFormField(
        new string[] { "TX Text Control .NET Server License", "Priority Support - 12 Months" }, 0)
    {
        Name = "Product",
        Editable = false
    };

    // A date picker with a date and the default width
    DateFormField invoiceDate = new DateFormField(new DateTime(2025, 5, 27), 0)
    {
        Name = "InvoiceDate"
    };

    // An unchecked check box
    CheckFormField termsAccepted = new CheckFormField(false)
    {
        Name = "TermsAccepted"
    };

    FormField[] fields = { customer, product, invoiceDate, termsAccepted };

    for (int row = 1; row <= fields.Length; row++)
    {
        // Selection positions start at 0, TableCell.Start at 1
        tx.Select(table.Cells.GetItem(row, 2).Start - 1, 0);

        if (!tx.FormFields.CanAdd || !tx.FormFields.Add(fields[row - 1]))
        {
            Console.WriteLine($"{fields[row - 1].Name} couldn't be added.");
        }
    }

    // A text for screen readers and accessible PDF documents
    customer.DescriptiveText = "Name of the customer";

    tx.Save("form.tx", StreamType.InternalUnicodeFormat);
    tx.Save("form.docx", StreamType.WordprocessingML);
    tx.Save("form.pdf", StreamType.AdobePDF);
}

The code works as follows:

  • The TextFormField constructor takes the width of the empty field in twips, 1/1440 inch; 0 selects a default width.
  • The SelectionFormField constructor takes the items and the zero-based index of the selected item, or -1 for no selection. An item can't be an empty string. Editable set to false makes the field a drop-down list; with true, users can also type text, as in a combo box.
  • The DateFormField constructor takes the date and the width of the empty field. The date is formatted with the culture of the text at the field's position.
  • The CheckFormField constructor takes the checked state.
  • CanAdd is false where no field can be inserted, for example inside another field. FormFieldCollection.Add returns true if the field was inserted.

Formats That Keep Form Fields

Which kind of form field a saved document contains depends on its format:

FormatForm fields become
TX Text Control format (.tx)TX Text Control form fields
DOCXContent controls, which MS Word shows as form controls
RTF and DOCLegacy form fields of MS Word; DOC is available with the Classic engine only
PDFPDF form fields with their names and values, so the form can be filled out in a PDF reader

When DOCX, DOC and RTF files are loaded, their content controls and legacy form fields become form fields again, unless LoadSettings.ApplicationFieldFormat is set to MSWord, as Content Controls explains. The values of PDF forms that come back filled out are read with Forms.GetAcroFormFields, described in PDF Forms.

Read and Check the Values of a Filled Form

A form filled out in the Document Viewer can be saved with its values, as Save the Filled Form describes, and sent to your server. The following program loads such a form, saved as Documents/form-filled.tx, collects the value of every form field in headers, footers and text frames too, prints the values as JSON and lists the fields that are empty:

using System.Text.Json;
using TXTextControl;

Dictionary<string, object?> values = new Dictionary<string, object?>();
List<string> missing = new List<string>();

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Load("Documents/form-filled.tx", StreamType.InternalUnicodeFormat);

    // The main text, text frames, headers and footers
    foreach (IFormattedText textPart in tx.TextParts)
    {
        foreach (FormField field in textPart.FormFields)
        {
            string name = string.IsNullOrEmpty(field.Name) ? $"Field {field.ID}" : field.Name;

            switch (field)
            {
                case CheckFormField checkBox:
                    values[name] = checkBox.Checked;
                    break;

                case DateFormField dateField:
                    values[name] = dateField.Date?.ToString("yyyy-MM-dd");
                    if (dateField.Date == null) missing.Add(name);
                    break;

                default:
                    // Text boxes and selection fields: the text the field shows
                    values[name] = field.Text;
                    if (string.IsNullOrEmpty(field.Text)) missing.Add(name);
                    break;
            }
        }
    }
}

Console.WriteLine(JsonSerializer.Serialize(values, new JsonSerializerOptions { WriteIndented = true }));

foreach (string name in missing)
{
    Console.WriteLine($"Missing value: {name}");
}

The code works as follows:

  • ServerTextControl.TextParts contains the main text, the text frames and the headers and footers. Each of them has a FormFields collection of its own; ServerTextControl.FormFields contains only the form fields of the main text.
  • The switch statement reads the value that fits each type: Checked for check boxes, Date for date pickers and, for text boxes and selection form fields, Text, the text the field shows.

The Document Editor checks values while users fill out a form, with conditional instructions designed in its Form Validation group, as Validate Form Fields describes, and the Document Viewer checks that the required fields are completed, as Validate and Submit the Values shows. Conditional instructions run only in the form protection mode, which needs an editor's EditMode, so code on the server checks the values it receives itself, as the program above does for empty fields.

Conditional Instructions and Radio Button Groups

A conditional instruction is a rule stored in the document: when the values of form fields meet its conditions, it changes other fields, for example enables them, sets their values or marks them as invalid. Users create these rules in the Document Editor, and they run in the Document Editor and the Document Viewer. ServerTextControl keeps them when a form is saved in the TX Text Control format, but it can't read, add or apply them, as Where Conditional Instructions Run explains.

A radio button group, from which users choose exactly one option, is built from check box fields and conditional instructions. The server can create the check boxes with the look of radio buttons:

using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    tx.Selection.Text = "Shipping method:\r\n";

    string[] options = { "Standard", "Express" };

    foreach (string option in options)
    {
        CheckFormField radioButton = new CheckFormField(false)
        {
            Name = "Shipping" + option
        };

        tx.FormFields.Add(radioButton);

        // Radio button characters, set after the field was added
        radioButton.CheckedCharacter = '\x25c9';
        radioButton.UncheckedCharacter = '\x2b55';

        tx.Selection.Text = " " + option + "\r\n";
    }

    tx.Save("shipping-form.tx", StreamType.InternalUnicodeFormat);
}

CheckedCharacter and UncheckedCharacter are the characters that show the two states, here U+25C9 (a filled circle) and U+2B55 (an empty circle). Set them after Add: set before, in the object initializer, they are stored, but the field shows the default check box character.

The group behavior comes from two conditional instructions for each button, designed in the Document Editor: one unchecks the other buttons, and one keeps a checked button from being cleared. Radio Button Groups lists both rules.

Flatten Form Fields

Before a filled form is archived or sent, you can replace its form fields with their values, so that nobody can change them. This is called flattening: for each text part, textPart.GetTextFields(TextFieldType.FormFields).Clear(true) removes the form fields and keeps their text. Flatten Forms shows the code for all fields, some fields and fields of one type, and flattening during a merge or before signing.

Find Form Fields and React to Changes

The blog article Automatically Mapping TX Text Control Form Fields to JSON Data in .NET C# renames form fields to match the property names of JSON data.

Next Steps