TextControlDocument
Inside the template of the WPF TextControl, the document is drawn on an element of its own: a TextControlDocument, a Canvas exactly the size of the document. Code that needs a WPF element for the document, for example to bind another control to its size, finds it here. It is not a data object, though: it holds neither the text nor the formatting, and binding it doesn't bind the content.
What TextControlDocument Is
The TextControl is a System.Windows.Controls.Control whose template consists of named parts. Its class declaration lists them with the TemplatePart attribute:
| Part | Type | Role |
|---|---|---|
TXPART_Border | Border | The border of the control |
TXPART_ScrollViewer | ScrollViewer | Shows the visible part of the document |
TXPART_Document | TextControlDocument | The document itself |
TextControlDocument derives from System.Windows.Controls.Canvas. Its size is the size of the whole document, and the ScrollViewer around it decides which part of it is on screen. The class was introduced with version 22.0 and has no members of its own: everything it offers comes from Canvas, Panel and FrameworkElement, such as ActualWidth and ActualHeight.
What the document contains is reached through the TextControl, not through this element: Load, Save, Selection and the collections of tables, images and fields. Binding Document Content connects the content to a view model.
Get the Part in Code
A template part exists once WPF has applied the template, which has happened when the Loaded event of the control occurs. Template.FindName then returns the part by its name. The window in MainWindow.xaml has a TextBlock below the editor:
<Window x:Class="MyWPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WPF="clr-namespace:TXTextControl.WPF;assembly=TXTextControl.WPF"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<TextBlock x:Name="sizeTextBlock" DockPanel.Dock="Bottom" Margin="4" />
<WPF:TextControl x:Name="textControl1" Loaded="TextControl1_Loaded" />
</DockPanel>
</Window>
The handler in MainWindow.xaml.cs finds the document part and binds the TextBlock to its height:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using TXTextControl.WPF;
namespace MyWPFApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextControl1_Loaded(object sender, RoutedEventArgs e)
{
// The template is applied: find the canvas that shows the document
TextControlDocument document =
textControl1.Template.FindName("TXPART_Document", textControl1) as TextControlDocument;
if (document == null)
{
return;
}
// Show the height of the canvas below the editor
Binding heightBinding = new Binding("ActualHeight")
{
Source = document,
StringFormat = "Document height: {0:F0} device-independent units"
};
sizeTextBlock.SetBinding(TextBlock.TextProperty, heightBinding);
}
}
}
The code works as follows:
Template.FindNamesearches the template oftextControl1for the nameTXPART_Document. Theascast returnsnullif the part isn't there, for example in a template of your own that leaves it out.ActualHeightis a read-only dependency property of every WPF element, measured in device-independent units of 1/96 inch. ABindingwith the canvas as itsSourceupdates theTextBlockwhenever the height changes, for example when the text grows by a page.- The binding is created in code because the part has no name in the scope of the window:
ElementName=TXPART_Documentin XAML doesn't find an element inside the template of another control.
Run the application: the text below the editor shows the height of the document and changes when the document grows or shrinks.
Get the Part in a Derived Control
A class derived from TextControl gets its parts at the moment the template is applied, by overriding OnApplyTemplate and calling GetTemplateChild:
using TXTextControl.WPF;
namespace MyWPFApp
{
public class DocumentTextControl : TextControl
{
// The canvas of the document, available after the template is applied
public TextControlDocument DocumentPart { get; private set; }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
DocumentPart = GetTemplateChild("TXPART_Document") as TextControlDocument;
}
}
}
Call base.OnApplyTemplate() first, so that the TextControl handles its template as before. The derived control is declared in XAML with the prefix of your project's namespace, such as <local:DocumentTextControl x:Name="textControl1" /> with xmlns:local="clr-namespace:MyWPFApp".
Positions in the Document
Positions in a document are measured in twips, a twentieth of a point, from its top-left corner. The TextControl converts between these document coordinates and its own coordinate system, and reports which part of the document is visible:
| Member | What it does |
|---|---|
| PointToDocument | Converts a System.Windows.Point in the coordinate system of the TextControl into document coordinates in twips |
| PointFromDocument | Converts a point in document coordinates into the coordinate system of the TextControl |
| ScrollLocation | The top-left corner of the visible part, in twips from the top-left corner of the document; available at run time only |
| VScroll, HScroll | Occur when the vertical or horizontal scroll position has changed |
Use these members of the TextControl for positions in the text rather than the offsets of the ScrollViewer part: they are documented in twips, the unit of every other position in TX Text Control. The following handler shows how far the user has scrolled, in inches (1,440 twips each):
private void TextControl1_VScroll(object sender, System.EventArgs e)
{
double inches = textControl1.ScrollLocation.Y / 1440.0;
sizeTextBlock.Text = $"Scrolled {inches:F1} inches down";
}
Connect it with VScroll="TextControl1_VScroll" on the TextControl element in XAML.
Replace the Template
The three names are the parts the TextControl looks for in its template. No source of Text Control describes a template of your own for the control, so keep the default template and change the look through the properties and styles described in Styles, Resources, and Themes, such as DisplayColors for the colors around the pages.
Next Steps
- Binding Document Content: the content and the formatting of the document in bindings.
- Value Converters: twips, colors and zoom factors for WPF controls.
- Control Initialization and Document Access: when the control and its parts are ready.
- WPF Layout and Resizing: the size of the
TextControlin the window.