Document Protection and Editable Regions
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 withReadAndSelectby 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:
| Property | When true |
|---|---|
| ReadOnly | The text and its formatting can't be changed |
| AllowFormatting | The document can be formatted |
| AllowFormattingStyles | Styles can be applied, if AllowFormatting is also true |
| AllowCopy | Content can be copied to the clipboard |
| AllowPrinting | The document can be printed |
| AllowEditingFormFields | Form 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:
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.
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:

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

Click Enforce Protection and enter a password.
Clicking Enforce Protection again asks for the password and removes the protection:

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.
nullor 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, orCombinedif 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:
| Property | Tells whether |
|---|---|
| CanEdit | Text or formatting can be changed |
| CanPrint | The document can be printed |
| CanCopy | Selected text may be copied |
| CanCharacterFormat, CanParagraphFormat | The selection can be formatted |
| CanStyleFormat | Styles can be applied |
| CanDocumentFormat | Page and section formatting, such as margins and page size, can be changed |
| CanEditFormFields | Form fields can be filled in |
Work With Editable Regions
| Member | Purpose |
|---|---|
| GetItems | The regions at the input position, or null |
| Remove | Removes a region and keeps its text; with true as the second parameter, only the selected part, which can split the region |
| EditableRegionHighlightMode | Highlights all regions while they contain the input position (Activated, the default), always or never |
| HighlightMode, HighlightColor | The highlighting of one region; the System.Drawing.Color needs an alpha value below 255 |
| ScrollTo, Text | Moves 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
- Document Protection: what document protection guards against, and what not.
- Form Fields and Conditional Instructions: forms that are filled in while the rest is protected.
- Passwords and Access Permissions: protection of PDF files.
- Comments: comments by the users set in
UserNames.