TX Text Control .NET for WPF

Comments and Tracked Changes

Topic

When a document is reviewed, two kinds of markup build up: comments that reviewers attach to pieces of text, and tracked changes, insertions and deletions that stay visible with their author and time until someone accepts or rejects them. The WPF TextControl records both while users work, compatible with MS Word. Code-behind accepts or rejects the changes of chosen reviewers throughout the document, produces a clean final version and can mark the differences between two versions as tracked changes.

Code on this page goes into MainWindow.xaml.cs with the TextControl named textControl1. It processes the markup of a whole document at once: every text part, every author, and the final version without markup.

MainWindow.xaml.cs needs these using directives:

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using TXTextControl;

How Comments and Tracked Changes Work

A comment is a CommentedText in the Comments collection, a tracked change a TrackedChange in TrackedChanges. Both collections belong to the text part that has the input focus, which can be the main text, a text frame, or a header or footer, so code that processes a whole document goes through all text parts, as the sections below do. The TX Text Control format, RTF, DOC and DOCX keep comments and tracked changes.

The basics are explained in the Document Editor section:

  • Track Changes: the author from UserNames, turning tracking on with IsTrackChangesEnabled, the properties of a change, accepting and rejecting changes in the active text part, operations that can't be tracked, events and highlighting.
  • Comments: adding comments to the selection or to a range given by a one-based start and a length, replies, the properties of a comment, removing comments, CommentHighlightMode and events.
  • Built-in Dialog Boxes: the modeless CommentsDialog and TrackedChangesDialog windows.

Accept or Reject Tracked Changes

A tracked change disappears only through TrackedChangeCollection.Remove: true accepts it, false rejects it, which removes inserted text and restores deleted text. This handler keeps the changes of a trusted reviewer and rejects the rest in all parts of the document:

private void AcceptReviewer_Click(object sender, RoutedEventArgs e)
{
    string trustedAuthor = "Alice Johnson";

    foreach (IFormattedText textPart in textControl1.TextParts)
    {
        // Work on a copy, because Remove changes the collection
        List<TrackedChange> changes = textPart.TrackedChanges.Cast<TrackedChange>().ToList();

        foreach (TrackedChange change in changes)
        {
            textPart.TrackedChanges.Remove(change, change.UserName == trustedAuthor);
        }
    }
}

TextParts contains the main text, text frames, headers and footers as IFormattedText objects, each with its own TrackedChanges and Comments.

Produce a Clean Final Version

Before a contract goes to the customer, the markup has to go. This handler stops tracking, accepts every change, deletes every comment and saves a PDF:

private void Finalize_Click(object sender, RoutedEventArgs e)
{
    textControl1.IsTrackChangesEnabled = false;

    foreach (IFormattedText textPart in textControl1.TextParts)
    {
        // Index 1 is always the first remaining element
        while (textPart.TrackedChanges.Count > 0 && textPart.TrackedChanges.Remove(textPart.TrackedChanges[1], true)) { }
        while (textPart.Comments.Count > 0 && textPart.Comments.Remove(textPart.Comments[1])) { }
    }

    textControl1.Save("contract-final.pdf", StreamType.AdobePDF);
}

CommentCollection.Remove deletes a comment and its replies but keeps the commented text. Each loop ends when nothing is left or a Remove call returns false.

LINQ queries that filter changes by author, kind and date are shown in the blog article Inspect and Process Track Changes in DOCX Documents with TX Text Control with .NET C#.

Compare Two Documents

Tracked changes can also show how two versions of a document differ. Text Control's comparison sample, written for Windows Forms, loads the original and the revised version into two TextControl instances, compares their paragraphs sentence by sentence and word by word, and writes each differing word of the revised version into the original while IsTrackChangesEnabled is true. Every difference then appears in the original as a deletion and an insertion, which the reviewer accepts or rejects as described above.

The comparison uses only Paragraphs, their Text and Start, and the Selection of the original, which the WPF TextControl and ServerTextControl have as well. It compares the paragraphs in their order and the words of each sentence by their position. The blog article Word-based Document Comparison and Track Changes Using TX Text Control and C# explains the algorithm, and the complete sample is in the GitHub repository TXTextControl.Windows.Forms.Comparison.

Next Steps