TX Text Control .NET for WPF

Document Protection and Editable Regions

Topic

How do you let a user fill in the blanks of a contract without letting them rewrite the clauses? The WPF TextControl protects a document so that it can only be read, or edited within set limits, and marks editable regions that stay open for everyone or for one named user. Users configure all of this on the Permissions tab of the ribbon; your code does it through a few properties and collections, and the settings are saved in the document.

The examples extend a window with a TextControl named textControl1, as in Create Your First WPF Application. The limits of this kind of protection outside the editor are the subject of Document Protection.

How Document Protection Works

Protection starts with EditMode:

  • Edit: no restrictions.
  • ReadAndSelect: the text can be read and selected, and editing and formatting are restricted by the document permissions.
  • ReadOnly: the text is displayed only; it can't even be selected.
  • UsePassword: combined with ReadAndSelect by the bitwise OR operator, it opens a dialog box in which a password for the restrictions is entered.

DocumentPermissions returns the DocumentPermissions object with the restrictions of ReadAndSelect. Each of its properties defaults to true:

PropertyWhen true
ReadOnlyThe text and its formatting can't be changed
AllowFormattingThe document can be formatted
AllowFormattingStylesStyles can be applied, if AllowFormatting is also true
AllowCopyContent can be copied to the clipboard
AllowPrintingThe document can be printed
AllowEditingFormFieldsForm fields can be filled in

RTF, DOC, DOCX, PDF and the TX Text Control format (TX) store the permissions and the password in encrypted form. When a document with a password is loaded, it is read-only, and only the same password sets the edit mode back to Edit.

In ReadAndSelect mode, editable regions can still be changed. A region belongs to a user name, or to everyone when the name is empty, and it is highlighted in a color per user. Which users work with the editor is set by UserNames.

Protect a Document With the Ribbon

The Permissions tab is a RibbonPermissionsTab in the ribbon of the window. The TextControl finds it by name through its RibbonPermissionsTab property; Connecting Ribbon Tabs in XAML shows the markup. With the tab, a user protects a document like this:

  1. Choose in Restrict Formatting and Restrict Editing what stays allowed, for example Allow Printing, Allow Copy or Fill in Form Fields, or click Read Only.

  2. Click Add Users in the Mark Exceptions and set Users group, type a name under New User, click Add and close the dialog box with OK:

    The WPF Permissions tab with the Add Users dialog box, which contains a New User box, an Add button and the user John in the Users list.

  3. Select text and check the user in the list of the group, or Every User, to make the selection editable:

    The WPF Permissions tab with the user John checked and the selected words general section highlighted in the document.

  4. Click Enforce Protection and enter a password.

Clicking Enforce Protection again asks for the password and removes the protection:

The Unprotect Document dialog box of the WPF sample asking for the password, with an editable paragraph highlighted in green in the document.

The group Edit restricted Document also switches the highlighting of the regions on and off and moves to the previous or next region.

Your application can fill the Add Users dialog box with its own user list. RegisteredUserNames sets the names offered, and AllowAddingUserNames with false keeps users from typing others. Here, the tab has the x:Name ribbonPermissionsTab1:

// Offer only the users of the application in the Add Users dialog box
ribbonPermissionsTab1.RegisteredUserNames = new string[] { "Tim Typer", "Ryan Reviewer" };
ribbonPermissionsTab1.AllowAddingUserNames = false;

Mark Editable Regions in Code

The regions of a document are in the EditableRegionCollection that EditableRegions returns. Add inserts a new EditableRegion. Both classes are in the TXTextControl namespace:

private void MarkSelectionButton_Click(object sender, RoutedEventArgs e)
{
    // Everyone may edit the selected text; the region has the ID 1
    EditableRegionCollection.AddResult result =
        textControl1.EditableRegions.Add(new EditableRegion(null, 1));

    if (result == EditableRegionCollection.AddResult.NoSelection)
    {
        MessageBox.Show("Select the text that should stay editable.");
    }
}

private void MarkForUserButton_Click(object sender, RoutedEventArgs e)
{
    // Only Tim Typer may edit characters 1 to 20; the start is one-based
    textControl1.EditableRegions.Add(new EditableRegion("Tim Typer", 2, 1, 20));
}

What the code does:

  • The user name comes first. null or an empty string opens the region to everyone.
  • The identifier is yours to choose, 1 or higher, or 0 for none. GetItem looks a region up by it.
  • Without a start and a length, the region covers the selection. Otherwise it covers the range, counted from 1.
  • EditableRegionCollection.AddResult tells the outcome, such as Successful, NoSelection, or Combined if the region was merged with an overlapping or adjacent one of the same user and ID.

Protect the Document in Code

With the regions in place, set the permissions and change the edit mode. UsePassword makes the editor ask the user for a password:

private void ProtectButton_Click(object sender, RoutedEventArgs e)
{
    // What remains allowed outside the editable regions
    textControl1.DocumentPermissions.ReadOnly = true;
    textControl1.DocumentPermissions.AllowPrinting = true;
    textControl1.DocumentPermissions.AllowCopy = false;

    // Protect the document; a dialog box asks for the password
    textControl1.EditMode = TXTextControl.EditMode.ReadAndSelect | TXTextControl.EditMode.UsePassword;

    textControl1.Save("contract.docx", StreamType.WordprocessingML);
}

Without UsePassword, the same restrictions apply, but setting EditMode back to Edit lifts them without a password.

Open a Protected Document

The user names have to be set before the document is loaded, for example in the Loaded handler of the control. The regions of these users and those open to everyone are then editable:

private void TextControl1_Loaded(object sender, RoutedEventArgs e)
{
    // The regions of these users can be edited
    textControl1.UserNames = new string[] { "Tim Typer" };

    textControl1.Load("contract.docx", StreamType.WordprocessingML);
}

The first user name is also recorded as the author of tracked changes and comments; see Track Changes. Setting EditMode to EditMode.Edit in code removes the protection; for a document with a password, the editor asks for it first.

Check What the User May Do

Read-only properties of the TextControl tell what the current edit mode and permissions allow. Check them before your own commands act, or use them to enable and disable buttons:

private void PrintButton_Click(object sender, RoutedEventArgs e)
{
    if (!textControl1.CanPrint)
    {
        MessageBox.Show("This document cannot be printed.");
        return;
    }

    textControl1.Print("contract", false);
}

Print opens the print dialog; Printing and Print Preview describes its parameters. The other properties:

PropertyTells whether
CanEditText or formatting can be changed
CanPrintThe document can be printed
CanCopySelected text may be copied
CanCharacterFormat, CanParagraphFormatThe selection can be formatted
CanStyleFormatStyles can be applied
CanDocumentFormatPage and section formatting, such as margins and page size, can be changed
CanEditFormFieldsForm fields can be filled in

Work With Editable Regions

MemberPurpose
GetItemsThe regions at the input position, or null
RemoveRemoves a region and keeps its text; with true as the second parameter, only the selected part, which can split the region
EditableRegionHighlightModeHighlights all regions while they contain the input position (Activated, the default), always or never
HighlightMode, HighlightColorThe highlighting of one region; the System.Drawing.Color needs an alpha value below 255
ScrollTo, TextMoves the input position to the region; returns its text

Four events report regions: EditableRegionCreated, EditableRegionDeleted, EditableRegionEntered and EditableRegionLeft. Connect a handler in XAML with EditableRegionEntered="TextControl1_EditableRegionEntered". It receives EditableRegionEventArgs with the region in EditableRegion:

private void TextControl1_EditableRegionEntered(object sender, EditableRegionEventArgs e)
{
    string user = string.IsNullOrEmpty(e.EditableRegion.UserName) ? "everyone" : e.EditableRegion.UserName;
    Title = $"Editable by {user}";
}

Headers, footers and text frames keep their regions in their own collections, HeaderFooter.EditableRegions and TextFrame.EditableRegions. A SaveSettings object with OmittedContent set to OmittedContent.EditableRegions saves a document without the region markers but with their text.

Next Steps