Fields and Subtext Parts
How does an application find the customer name in a letter again, open the link a user clicks, or know that the cursor is inside the payment clause of a contract? Fields and subtext parts answer these questions. Fields mark short pieces of text such as names, links, bookmarks and MS Word fields; subtext parts mark longer ranges and can carry data. In a loaded document, your code collects the fields of every text part, reads and changes MS Word fields with typed field adapters and keeps data with ranges of text.
The code goes into MainWindow.xaml.cs of a window whose TextControl is named textControl1 and which shows results in a TextBlock named statusText. Merge fields filled with data are described in Merge Fields, fillable form fields in Forms and Conditional Instructions and page numbers in Sections, Pages, Headers, and Footers.
MainWindow.xaml.cs needs these using directives:
using System.Text;
using System.Windows;
using TXTextControl;
using TXTextControl.DocumentServer.Fields;
The Kinds of Fields
Every field is a marked piece of text with properties such as a name and an ID, and each kind is kept in its own collection of WPF.TextControl: TextFields, ApplicationFields for MS Word fields, HypertextLinks, DocumentTargets for bookmarks and DocumentLinks. Fields, Hyperlinks, and Bookmarks explains each kind and shows how to insert, fill, find and remove text fields, insert links and bookmarks, handle the field events and handle clicks on links. Keep in mind:
- Only the active text part. The collections contain the fields of the text part with the input focus, which is the main text, a text frame, or a header or footer. TextParts gives you every part, as the next section shows.
- MS Word fields are imported on request. Set LoadSettings.ApplicationFieldFormat before loading a Word document, or
ApplicationFieldsstays empty. - Names and IDs of text fields are kept in the TX Text Control format,
StreamType.InternalUnicodeFormat. - Links don't navigate by themselves. Open only web addresses from a
HypertextLinkClickedhandler, so that documents can't start programs; see Handling Untrusted Documents.
Find the Fields of All Text Parts
TextParts contains the main text, the text frames and the headers and footers, each as an IFormattedText with its own field collections. This handler loads a Word template with its fields and lists the merge fields and links of every part:
private void ListFields_Click(object sender, RoutedEventArgs e)
{
LoadSettings loadSettings = new LoadSettings()
{
ApplicationFieldFormat = ApplicationFieldFormat.MSWord
};
textControl1.Load("template.docx", StreamType.WordprocessingML, loadSettings);
StringBuilder list = new StringBuilder();
foreach (IFormattedText textPart in textControl1.TextParts)
{
foreach (ApplicationField field in textPart.ApplicationFields)
{
list.AppendLine($"{textPart.GetType().Name}: {field.TypeName} = \"{field.Text}\"");
}
foreach (HypertextLink link in textPart.HypertextLinks)
{
list.AppendLine($"{textPart.GetType().Name}: link to {link.Target}");
}
}
statusText.Text = list.ToString();
}
TypeName is the type of a Word field, and Parameters holds the rest of its field code. The field adapters in Use Field Adapters for MS Word Fields read and write these settings with typed properties.
ApplicationFieldCollection.Clear with true removes all fields of a collection and keeps their text, which turns them into plain text. Remove(field, true) does this for one field.
Use Field Adapters for MS Word Fields
An ApplicationField gives you the type name and the raw parameters of a Word field, but it doesn't know what they mean. The classes of the TXTextControl.DocumentServer.Fields namespace are adapters for the common Word fields: an adapter wraps an application field and offers its settings as typed properties, so your code doesn't have to parse or build field codes.
- Change an existing field: pass the application field to the adapter's constructor, for example
new MergeField(field). Changes to the adapter's properties change the field in the document. - Create a new field: create the adapter without parameters, set its properties and add its ApplicationField with
textControl1.ApplicationFields.Add.
Every adapter class has the constant TYPE_NAME, the type name of the Word field it handles. Comparing it with TypeName tells you which adapter fits a field:
| Adapter | Word field | Described in |
|---|---|---|
| MergeField | MERGEFIELD, a value from the data | Merge Fields |
| DateField | The current date in a format | Merge Fields |
| IfField | IF, text that depends on a comparison | Conditional Content |
| IncludeText | INCLUDETEXT, another document included in a template | Subtemplates |
| NextField, NextIfField | NEXT and NEXTIF, which continue with the next data record, always or on a condition | Overview of Mail Merge and Reporting |
| FormText, FormCheckBox, FormDropDown | The legacy form fields of MS Word | Content Controls |
| ContentControlFieldAdapter and its six derived classes | The content controls of MS Word | Content Controls |
The first six derive from MailMergeFieldAdapter, the legacy form fields from FormFieldAdapter, and all of them from FieldAdapter. The following handler shows the settings of the Word field at the input position:
private void ShowFieldSettings_Click(object sender, RoutedEventArgs e)
{
// The application field at the input position, or null
ApplicationField field = textControl1.ApplicationFields.GetItem();
if (field == null)
{
statusText.Text = "No MS Word field at the input position";
}
else if (field.TypeName == MergeField.TYPE_NAME)
{
MergeField mergeField = new MergeField(field);
statusText.Text = $"Merge field {mergeField.Name}, text before: \"{mergeField.TextBefore}\"";
}
else if (field.TypeName == IfField.TYPE_NAME)
{
IfField ifField = new IfField(field);
statusText.Text = $"IF {ifField.Expression1} {ifField.Operator} {ifField.Expression2}";
}
else if (field.TypeName == NextIfField.TYPE_NAME)
{
NextIfField nextIfField = new NextIfField(field);
statusText.Text = $"NEXTIF {nextIfField.Expression1} {nextIfField.Operator} {nextIfField.Expression2}";
}
else
{
statusText.Text = $"{field.TypeName} field: {string.Join(" ", field.Parameters)}";
}
}
The code works as follows:
- ApplicationFieldCollection.GetItem without a parameter returns the field at the input position, or
null. - The constructors, such as the MergeField constructor, connect the adapter to the field. Name and TextBefore of
MergeFieldare settings of the field code. - Expression1, Operator and Expression2 of
IfFieldform the comparison.NextIfFieldhas the same three properties, and its Result is the outcome of the comparison. NextField has no settings of its own. - Other fields fall back to the raw
Parameters.
FieldAdapter.ShowDialog opens the options dialog box of a field; in a WPF application, pass the Window as its owner, for example mergeField.ShowDialog(this). Reporting Ribbon and WPF Dialog Boxes opens it for the field at the input position.
Mark Ranges With Subtext Parts
A SubTextPart covers a continuous range of text, tables and fields included, and stores a name, an ID and a Data string. Subtext parts nest but never overlap, and Merge Blocks are built on them. Subtext Parts shows how to add, list, find, save and remove them and how to handle their events. The Data string survives only in the TX Text Control format; DOCX, DOC and RTF files keep the ranges, which come back as subtext parts when LoadSettings.LoadSubTextParts is true. The blog article The Power of SubTextParts: Typical Use Cases collects further uses; its code is written for the Windows Forms TextControl.
Next Steps
- Fields, Hyperlinks, and Bookmarks: insert, find and remove fields, links and bookmarks and handle their events.
- Subtext Parts: add, find, save and remove subtext parts.
- Merge Fields: fill merge fields with data.
- Content Controls: read and create the content controls and legacy form fields of MS Word.
- Forms and Conditional Instructions: fields that users fill out.