Architecture and Control Lifecycle
Code that runs at the wrong moment is a common source of trouble in an editor application: a document loaded before the form is shown, a selection changed in the wrong text part, a document lost when the form closes. Knowing what a TextControl holds, which part of the document your code works on, and when the control is created, ready and gone tells you where each piece of your code belongs.
The overview of the product's parts is in Product Architecture, and the control itself is introduced in TextControl. Read on for how the control and its document are organized and how a form goes through its life with an editor on it.
What a TextControl Holds
TextControl is a Windows Forms control derived from System.Windows.Forms.Control. The document it shows is not one block of text but several text parts: the main text, the text frames, the headers and footers, and the footnote sections. Each text part has its own lines, paragraphs, characters, tables, images and fields.
TextControl
+-- Main text the body of the document
+-- Text frames text in positioned frames
+-- Headers and footers the headers and footers of the sections
+-- Footnote sections the text of the footnotes
Document-wide: Sections, ParagraphStyles, InlineStyles, DocumentSettings, FormFields, TrackedChanges
All text parts implement the IFormattedText interface, and so do TextControl and ServerTextControl. That is why the same members, such as Selection, Paragraphs and Tables, appear on the control, on a TextFrame and on a HeaderFooter, and why code written for the editor also works with ServerTextControl.
The Text Part With the Input Focus
When you use Selection, Paragraphs or InputPosition of the TextControl, they refer to the text part that has the input focus. That is the main text, unless the user has clicked into a header, a footer or a text frame. Code that formats "the selection" therefore formats text in the header if the user is editing the header.
TextParts returns all text parts in a TextPartCollection: first the main text, then the text frames and then the headers and footers, each group in the order of their position in the document. The following method lists them and gives the main text the input focus again:
private void ListTextParts()
{
foreach (IFormattedText textPart in textControl1.TextParts)
{
Debug.WriteLine(textPart.GetType().Name + ": " + textPart.Paragraphs.Count + " paragraphs");
}
// Give the main text the input focus again
textControl1.TextParts.Activate(textControl1.TextParts.GetMainText());
}
- Each element of the collection is a MainText,
TextFrame,HeaderFooteror FootnoteSection object, and each can be used throughIFormattedText. - Activate moves the input focus to a main text, text frame or header or footer and scrolls it into view. GetMainText returns the main text.
- The method needs
using System.Diagnostics;forDebug.
Headers and Footers, Text Frames and Shapes and Footnotes show how to work with each kind of text part.
Runtime-Only Members
The reference marks many members of TextControl as "Runtime only", among them Selection, InputPosition and TextParts. They are not available in the Properties window: you use them in code while the application runs. Properties without this limitation, such as ViewMode or FormattingPrinter, can also be set in the designer.
The Life of the Control
A TextControl on a form goes through the same stages as the form. Unlike a ServerTextControl, it needs no call to Create: the form creates and releases it.
| Stage | What happens | Where your code goes |
|---|---|---|
| Created | InitializeComponent in the constructor of the form creates the control and applies the values of the Properties window. | The constructor, after InitializeComponent: connect event handlers and set properties. |
| Ready | The form is loaded and about to be shown. | The Load event of the form, or an override of OnLoad: load the first document, set the focus. |
| Working | Users and your code load, edit and save documents. | Event handlers such as Changed and DocumentLoaded. |
| Closing | The user closes the form. | The FormClosing event: ask whether to save changes, and cancel closing if needed. |
The TX Words sample that the setup installs follows this pattern: its main form connects its features to the TextControl in an override of OnLoad. For more control over the moment the window handle exists, TextControl also overrides OnHandleCreated of Control, which a class derived from TextControl can override in turn.
The following form puts the stages together. It assumes a TextControl named textControl1 on Form1 and a file report.docx in the folder of the application:
using System;
using System.Diagnostics;
using System.Windows.Forms;
using TXTextControl;
public partial class Form1 : Form
{
public Form1()
{
// Creates textControl1 with the settings of the Properties window
InitializeComponent();
textControl1.DocumentLoaded += textControl1_DocumentLoaded;
textControl1.ContentsReset += textControl1_ContentsReset;
textControl1.Changed += textControl1_Changed;
FormClosing += Form1_FormClosing;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// The form is about to be shown for the first time
textControl1.Load("report.docx", StreamType.WordprocessingML);
textControl1.Focus();
}
private void textControl1_DocumentLoaded(object sender, EventArgs e)
{
Debug.WriteLine("A document has been loaded.");
}
private void textControl1_ContentsReset(object sender, EventArgs e)
{
Debug.WriteLine("The contents have been reset.");
}
private void textControl1_Changed(object sender, EventArgs e)
{
Debug.WriteLine("The contents have changed.");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Ask the user to save changes here, and set e.Cancel to keep the form open
}
}
The code works as follows:
- Loading. Load replaces the complete contents of the control. DocumentLoaded occurs when a new document has been loaded, which is the moment to read its properties or update your user interface.
- Resetting. ResetContents deletes the entire contents, for example for a New command. ContentsReset occurs when all text has been deleted and all attributes have been reset to the state after initialization.
- Editing. Changed indicates that the contents of the document have changed. Use it to mark the document as modified.
- Closing. The document exists only in the memory of the control until your code saves it. The
FormClosinghandler is the last place to save it. Load and Save Documents in Getting Started shows a complete save-changes prompt.
Events lists the other events of the control.
One Engine for Display and Printing
TextControl formats the document with the text dimensions of a printer, which FormattingPrinter names, so that the document on the screen has the same layout as the printed one. The printer is contacted while the control initializes, so a slow printer can delay the start of the control. Printing lists the values of FormattingPrinter, and Performance Best Practices shows how to start without waiting for a printer.
Several Editors and Background Work
Each TextControl has its own document. A form with two editors, such as a document and a template side by side, keeps two independent documents, and nothing is shared unless your code copies it, for example by saving one document to a byte array and loading it into the other.
Work that the user doesn't watch, such as creating many documents or preparing a document before it is shown, belongs in a ServerTextControl. It has the same programming interface but no window. You create it in code, call Create before any other member, and dispose of it when the work is done. Using ServerTextControl in Desktop Applications and UI Threading and Background Operations go further.
Let the Control Grow With Its Content
A TextControl doesn't have to be a full editor. Used as a formatted input field on a form, it can change its own width or height as the user types, like a text box that grows with its text. AutoControlSize turns this on with an AutoSize object. It works only when ViewMode is ViewMode.SimpleControl, where the text is formatted to the extent of the control instead of to a page.
// The control grows and shrinks horizontally, up to 600 pixels
textControl1.ViewMode = ViewMode.SimpleControl;
textControl1.AutoControlSize.AutoExpand = AutoSizeDirection.Horizontal;
textControl1.AutoControlSize.AutoShrink = AutoSizeDirection.Horizontal;
textControl1.AutoControlSize.MaxSize = new Size(600, 600);
textControl1.HExpanded += (sender, e) => Debug.WriteLine("Wider: " + textControl1.Width);
textControl1.HShrunk += (sender, e) => Debug.WriteLine("Narrower: " + textControl1.Width);
The code works as follows:
- AutoExpand and AutoShrink take an AutoSizeDirection value:
None,Vertical,HorizontalorBoth. - MaxSize limits the growth and MinSize the shrinking, in pixels. The control never shrinks below the space of one character, and its width and height can't exceed
short.MaxValue. - HExpanded and HShrunk occur after the control has changed its width, VExpanded and VShrunk after it has changed its height. Use them to move the controls below or beside it.
Sizeneedsusing System.Drawing;.
The AutoSize constructor sets all four values at once for an assignment to AutoControlSize, and ResetAutoControlSize goes back to the default, a control that keeps its size.
Next Steps
- Loading and Saving Documents: all ways to get a document into the editor and out again.
- Selections and Caret Position: work with the selection and the input position.
- Events: react to what happens in the editor.
- Creating and Configuring Controls in Code: create a
TextControlwithout the designer.