TX Text Control .NET for WPF

Content Controls

Topic

A form built in MS Word, such as an order form or a checklist, holds its input elements as content controls or, in older documents, as legacy form fields. When such a DOCX file is loaded into the WPF TextControl, one load setting decides whether they become TX Text Control form fields, whose values are easy to read and set, or MS Word fields that keep Word's own settings, such as the title and tag of a content control. The field adapter classes of TXTextControl.DocumentServer.Fields read and write those settings and create new content controls in code.

The code goes into MainWindow.xaml.cs of a window with a TextControl named textControl1 and a TextBlock named statusText. Form fields created in TX Text Control are covered in Forms and Conditional Instructions, and the other kinds of MS Word fields in Fields and Subtext Parts.

MainWindow.xaml.cs needs these using directives:

using System;
using System.Text;
using System.Windows;
using TXTextControl;
using TXTextControl.DocumentServer.Fields;

What Content Controls and Legacy Form Fields Are

MS Word offers two kinds of input elements for forms:

  • Content controls, introduced with MS Word 2007, include rich text, plain text, picture, combo box, drop-down list, check box and date picker controls. Only DOCX files store them; the MS Word 97-2003 format doesn't.
  • Legacy form fields are the older text box, check box and drop-down list fields, stored as the fields FORMTEXT, FORMCHECKBOX and FORMDROPDOWN.

TX Text Control supports both. LoadSettings.ApplicationFieldFormat decides what they become when a document is loaded:

ApplicationFieldFormatContent controls and legacy form fields becomeOther MS Word fields become
None (default)Form fields in TextControl.FormFieldsNot imported as fields
MSWordTXFormFieldsForm fields in FormFieldsApplication fields
MSWordApplication fields in TextControl.ApplicationFieldsApplication fields

An ApplicationField gives access to the TypeName and the Parameters of a Word field. For content controls and legacy form fields, the parameters are Office Open XML markup. Every content control has the type name SDTBLOCK, and its markup names the kind of control. The adapters described below read and write this markup for you.

Read the Values of a Word Form

To read the values that users entered in MS Word, load the form as form fields. This handler opens order-form.docx and lists each field with its value:

private void ReadWordForm_Click(object sender, RoutedEventArgs e)
{
    LoadSettings loadSettings = new LoadSettings()
    {
        // Content controls and legacy form fields become form fields
        ApplicationFieldFormat = ApplicationFieldFormat.MSWordTXFormFields
    };

    textControl1.Load("order-form.docx", StreamType.WordprocessingML, loadSettings);

    StringBuilder values = new StringBuilder();

    foreach (FormField field in textControl1.FormFields)
    {
        switch (field)
        {
            case CheckFormField checkBox:
                values.AppendLine($"{checkBox.Name}: {(checkBox.Checked ? "checked" : "not checked")}");
                break;

            case DateFormField dateField:
                values.AppendLine($"{dateField.Name}: {dateField.Date?.ToShortDateString() ?? "(no date)"}");
                break;

            default:
                // Text boxes, combo boxes and drop-down lists show their value as text
                values.AppendLine($"{field.Name}: {field.Text}");
                break;
        }
    }

    statusText.Text = values.ToString();
}

A check box content control of the Word form arrives as a CheckFormField, and its Checked property is the state the user set in MS Word. A date picker becomes a DateFormField whose Date is null when no date was entered. Setting these properties and saving the document as DOCX fills the form; Forms and Conditional Instructions shows the other members of the form field classes.

How Field Adapters Work

A field adapter wraps an application field and turns its markup into typed properties: passed an existing application field, it reads and changes that field; created without a parameter, it makes a new field that ApplicationFieldCollection.Add inserts. Use Field Adapters for MS Word Fields explains both directions and how the constant TYPE_NAME chooses the adapter. These adapters handle the form elements of MS Word:

AdapterBase classWrapsTyped properties
FormTextFormFieldAdapterA legacy text boxMaxLength, Format, Type (regular text, number, date, current date, current time or calculation)
FormCheckBoxFormFieldAdapterA legacy check box, FORMCHECKBOXChecked, Size, SizeAuto
FormDropDownFormFieldAdapterA legacy drop-down list, FORMDROPDOWNListEntries
PlainTextContentControl, RichTextContentControlContentControlFieldAdapterA plain text or rich text content controlText
CheckBoxContentControlContentControlFieldAdapterA check box content controlChecked
ComboBoxContentControl, DropDownListContentControlContentControlFieldAdapterA combo box or drop-down list content controlListItems of the combo box, ListItems of the drop-down list
DateContentControlContentControlFieldAdapterA date picker content controlDate, DateFormat, LanguageID, Calendar, StoreMappedDataAs

Read and Change Legacy Form Fields

Loaded with ApplicationFieldFormat.MSWord, legacy form fields keep their Word settings, such as the help text shown for a field. This handler lists the legacy form fields of order-form.docx and checks every check box:

private void ReadLegacyFormFields_Click(object sender, RoutedEventArgs e)
{
    LoadSettings loadSettings = new LoadSettings()
    {
        // All MS Word fields, form fields included, become application fields
        ApplicationFieldFormat = ApplicationFieldFormat.MSWord
    };

    textControl1.Load("order-form.docx", StreamType.WordprocessingML, loadSettings);

    StringBuilder list = new StringBuilder();

    foreach (ApplicationField field in textControl1.ApplicationFields)
    {
        if (field.TypeName == FormCheckBox.TYPE_NAME)
        {
            FormCheckBox checkBox = new FormCheckBox(field);
            checkBox.Checked = true;
            list.AppendLine($"Check box {checkBox.Name}, help text: {checkBox.HelpText}");
        }
        else if (field.TypeName == FormDropDown.TYPE_NAME)
        {
            FormDropDown dropDown = new FormDropDown(field);
            list.AppendLine($"Drop-down list {dropDown.Name}: {string.Join(", ", dropDown.ListEntries)}");
        }
        else if (field.TypeName == FormText.TYPE_NAME)
        {
            FormText textBox = new FormText(field);
            list.AppendLine($"Text box {textBox.Name}: {textBox.Text} (up to {textBox.MaxLength} characters)");
        }
    }

    statusText.Text = list.ToString();
}

The code works as follows:

  • TYPE_NAME of FormCheckBox, FormDropDown and FormText is compared with the type name of each field, so the code doesn't depend on how the type names are spelled.
  • The constructor with an application field connects the adapter to the field in the document. Setting Checked changes that field.
  • Name, Text and HelpText belong to the base class FormFieldAdapter, which also has StatusText, Enabled, CalcOnExit, Font and Bounds, in twips.
  • The ApplicationFields collection contains the fields of the text part with the input focus. For forms with fields in headers, footers or text frames, loop through TextParts as Fields and Subtext Parts shows.

Save the document with StreamType.WordprocessingML to keep the changed fields as MS Word form fields.

Create Content Controls in Code

The content control adapters create content controls that MS Word shows with their title and tag. This handler inserts a plain text control for the customer name, a date picker for the order date and a check box for the terms, each at the input position:

private void InsertContentControls_Click(object sender, RoutedEventArgs e)
{
    textControl1.Text = "";

    textControl1.Selection.Text = "Customer: ";
    PlainTextContentControl customerName = new PlainTextContentControl()
    {
        Title = "Customer name",
        Tag = "CustomerName",
        Text = "Acme Corporation"
    };
    textControl1.ApplicationFields.Add(customerName.ApplicationField);

    textControl1.Selection.Text = "\r\nOrder date: ";
    DateContentControl orderDate = new DateContentControl()
    {
        Title = "Order date",
        Tag = "OrderDate",
        Date = DateTime.Today
    };
    textControl1.ApplicationFields.Add(orderDate.ApplicationField);

    textControl1.Selection.Text = "\r\nI accept the terms: ";
    CheckBoxContentControl termsAccepted = new CheckBoxContentControl()
    {
        Title = "Terms accepted",
        Tag = "TermsAccepted",
        Checked = false
    };
    textControl1.ApplicationFields.Add(termsAccepted.ApplicationField);

    // DOCX is the format that keeps content controls
    textControl1.Save("order-form.docx", StreamType.WordprocessingML);
}

The code works as follows:

  • Each adapter is created with its constructor without parameters, such as the PlainTextContentControl constructor, which creates a new application field.
  • ApplicationFieldCollection.Add inserts the field at the input position, and the next Selection.Text continues behind it.
  • Opened in MS Word, order-form.docx shows three content controls. Loaded again with the default load settings, they are form fields in the TextControl.

The base class ContentControlFieldAdapter has the properties that all content controls share:

PropertyContains
Title, TagThe title that MS Word shows and the tag that code uses to find the control
IdThe ID of the content control
ContentEditable, ContentDeletableWhether the content can be edited and deleted
TypeThe type of the content control
PlaceholderThe placeholder part of the content control

To change a content control of an existing document, load it with ApplicationFieldFormat.MSWord and pass its application field to the constructor of the adapter of its kind, for example new CheckBoxContentControl(field). The blog article Working with Content Controls in MS Word Documents using TX Text Control in C# shows the import options side by side; its code uses ServerTextControl, whose collections are the same as those of the WPF TextControl.

Merge Data Into Word Forms

A form template can also be filled by a mail merge. MailMerge.FormFieldMergeType decides whether form fields keep their input elements with the merged values preselected or are replaced with the values; Merge Fields describes the options.

Next Steps