TX Text Control .NET for WPF

Embedding the Editor in Windows and UserControls

Topic

Real applications place the editor inside larger user interfaces: a reusable user control that bundles the editor with its bars, a window that shows a letter next to its cover note, or a document window opened for each file. The WPF TextControl fits into any of them like other WPF controls. Three things need attention: the license when the editor lives in a control library, the names by which the editor finds its bars, and the moment each editor is ready.

The samples use a WPF project with the package TXTextControl.TextControl.WPF.SDK and the XAML prefix WPF for the controls of TX Text Control, as in XAML Integration.

Choose a Container

ContainerUse it whenWhat to take care of
A WindowOne editor fills a windowThe layout; see WPF Layout and Resizing
A RibbonWindowThe window has a ribbon with a Quick Access Toolbar in its title barSee Create a WPF Ribbon Application
A UserControlThe same editor, with its bars and methods, appears in several windowsThe license, when the user control is in a control library; the names of the bars stay inside the user control
Several editors in one windowDocuments are shown side by sideOne set of bars that follows the editor with the focus, or one set per editor

Put the Editor Into a UserControl

A user control bundles the editor, its bars and the methods the window needs. The following EditorControl.xaml holds an editor with a button bar and a status bar:

<UserControl x:Class="MyWPFApp.EditorControl"
             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">
    <DockPanel>
        <WPF:ButtonBar x:Name="buttonBar1" DockPanel.Dock="Top" />
        <WPF:StatusBar x:Name="statusBar1" DockPanel.Dock="Bottom" />
        <WPF:TextControl x:Name="textControl1"
                         ButtonBar="buttonBar1" StatusBar="statusBar1"
                         Loaded="textControl1_Loaded" />
    </DockPanel>
</UserControl>

The code-behind, EditorControl.xaml.cs, gives the window methods to load and save the document. A document that arrives before the editor is loaded waits until it is:

using System.Windows;
using System.Windows.Controls;
using TXTextControl;

namespace MyWPFApp
{
    public partial class EditorControl : UserControl
    {
        private byte[] pendingDocument;

        public EditorControl()
        {
            InitializeComponent();
        }

        // Give the host access to the editor
        public TXTextControl.WPF.TextControl Editor => textControl1;

        // Load a document now, or as soon as the editor is ready
        public void LoadDocument(byte[] data)
        {
            if (textControl1.IsLoaded)
            {
                textControl1.Load(data, BinaryStreamType.InternalUnicodeFormat);
            }
            else
            {
                pendingDocument = data;
            }
        }

        public byte[] SaveDocument()
        {
            textControl1.Save(out byte[] data, BinaryStreamType.InternalUnicodeFormat);
            return data;
        }

        private void textControl1_Loaded(object sender, RoutedEventArgs e)
        {
            if (pendingDocument != null)
            {
                textControl1.Load(pendingDocument, BinaryStreamType.InternalUnicodeFormat);
                pendingDocument = null;
            }
        }
    }
}

The code works as follows:

  • The names buttonBar1 and statusBar1 are defined in the XAML of the user control, and the TextControl in the same XAML finds them. Every instance of the user control has its own editor and its own bars.
  • IsLoaded is true once WPF has loaded the editor into the window. A window typically calls LoadDocument in its constructor, before that, so the document is kept and loaded in the Loaded handler. Control Initialization and Document Access explains why.
  • Load and Save exchange the document as a byte array in BinaryStreamType.InternalUnicodeFormat, the internal format of TX Text Control, which suits a database field.
  • Editor gives the window direct access to the TextControl. Leave it out if the window should use only the methods of the user control.

A window declares the user control with a prefix for your own namespace and places it like any other control:

<Window x:Class="MyWPFApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyWPFApp"
        Title="MainWindow" Height="600" Width="1200">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <local:EditorControl x:Name="letterEditor" Grid.Column="0" />
        <local:EditorControl x:Name="coverNoteEditor" Grid.Column="1" />
    </Grid>
</Window>

In the constructor of the window, after InitializeComponent, a call such as letterEditor.LoadDocument(File.ReadAllBytes("letter.tx")) gives the left editor its document.

A UserControl in a Control Library

When the user control is in a separate control library, the license of TX Text Control is compiled into that library, not into the executable. Set TextControl.EntryAssembly to the library in the constructor of the user control, before InitializeComponent creates the TextControl:

// The license is compiled into this control library
TXTextControl.WPF.TextControl.EntryAssembly = typeof(EditorControl).Assembly;

InitializeComponent();

This works since version 32.0 with Service Pack 2. Application Licensing shows the complete control library. A user control in the application's own project doesn't need the line.

Share Bars Between Several Editors

Two editors in one window can share one button bar and one status bar, which follow the editor the user works with. The connection properties are names that can be changed at any time, so the window connects the bars to the editor that receives the focus:

<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="600" Width="1200">
    <DockPanel>
        <WPF:ButtonBar x:Name="buttonBar1" DockPanel.Dock="Top" />
        <WPF:StatusBar x:Name="statusBar1" DockPanel.Dock="Bottom" />
        <UniformGrid Columns="2">
            <WPF:TextControl x:Name="letterEditor" GotFocus="Editor_GotFocus" />
            <WPF:TextControl x:Name="coverNoteEditor" GotFocus="Editor_GotFocus" />
        </UniformGrid>
    </DockPanel>
</Window>

The handler in MainWindow.xaml.cs moves the connection:

private TXTextControl.WPF.TextControl activeEditor;

private void Editor_GotFocus(object sender, RoutedEventArgs e)
{
    TXTextControl.WPF.TextControl editor = (TXTextControl.WPF.TextControl)sender;
    if (editor == activeEditor) return;

    // Disconnect the bars from the editor that had the focus
    if (activeEditor != null)
    {
        activeEditor.ButtonBar = string.Empty;
        activeEditor.StatusBar = string.Empty;
    }

    // Connect them to the editor the user works with now
    editor.ButtonBar = "buttonBar1";
    editor.StatusBar = "statusBar1";
    activeEditor = editor;
}

The code works as follows:

  • GotFocus is the WPF event that occurs when an element receives the keyboard focus. Both editors use the same handler, and sender is the one the user clicked into.
  • ButtonBar and StatusBar set to an empty string disconnect the bars from the other editor, so that each bar is connected to one editor at a time.

The same works for ribbon tabs, through the ribbon tab properties of the TextControl.

Editors in Tabs and Other Windows

  • In a TabControl, WPF shows only the content of the selected tab and loads it into the window when the tab is selected. The Loaded event of an editor can therefore occur more than once; code in its handler that must run only once, such as loading the first document, keeps a flag, as Control Initialization and Document Access shows.
  • In a second window, an editor is independent of the one in the main window, with its own document and its own bars declared in the XAML of that window.

Next Steps