TX Text Control .NET for Windows Forms

Comments

Topic

A comment attaches a note to a piece of text without changing the text itself, the way reviewers annotate a draft in MS Word. Users of a TextControl add, answer, edit and delete comments with the context menu, the ribbon and a sidebar, and your code does the same with the CommentCollection. Read on for both, and for the events that tell your application when comments come and go.

The code below belongs in Form1.cs of a Windows Forms application whose editor is named textControl1, as in Create Your First Windows Forms Application.

How Comments Work

A comment consists of the commented text and the comment string, together a commented text. The editor highlights commented text in a color that depends on the author; the current user's comments are red. A comment belongs to the current user when it is added, which is the first name of UserNames. With an empty list, the author is unknown.

Comments can have replies. A reply refers to the same text but can come from another user at another time. Removing a comment also removes its replies, and never the commented text.

Comments are saved with the document and loaded again in the TX Text Control format (TX), RTF, DOC and DOCX, so comments made in MS Word show up in the editor and the other way around.

Comment Text as a User

Users select text and add a comment from the context menu or with Add Comment on the Proofing tab of the ribbon. An input box opens beside the text; the comment is added when the user closes it with a mouse click or the Enter key. The context menu of a comment has entries to edit, delete and reply to it.

The Comments sidebar lists all comments with their author and time:

TX Text Control Words with a comment balloon above the heading Protected Document and the Comments sidebar on the right showing the comment with the user name John and a date.

To open it from the Comments button of the tab, connect a sidebar with the CommentsSidebar property of the RibbonProofingTab; Sidebar and Panels shows how. Without a ribbon, the CommentsDialog shows the same list in a modeless dialog box:

private void commentsDialogButton_Click(object sender, EventArgs e)
{
    CommentsDialog dialog = new CommentsDialog(textControl1);
    dialog.Show();
}

Add Comments in Code

Comments returns the CommentCollection of the text part with the input focus. Its Add method has two overloads: without a parameter, it opens the input box for the selected text, and with a CommentedText, it inserts a prepared comment:

private void addCommentButton_Click(object sender, EventArgs e)
{
    // Comment the selected text
    CommentCollection.AddResult result =
        textControl1.Comments.Add(new CommentedText("Please check this figure."));

    if (result == CommentCollection.AddResult.NoSelection)
    {
        MessageBox.Show("Select the text you want to comment on.");
    }
}

private void commentHeadingButton_Click(object sender, EventArgs e)
{
    // Comment the first 20 characters; the start is one-based
    textControl1.Comments.Add(new CommentedText("Shorten the heading.", 1, 20));
}

The code works as follows:

  • new CommentedText("...") comments the current selection. Add returns a value of CommentCollection.AddResult: Successful, or a reason such as NoSelection, PositionInvalid or SelectionTooComplex, when the selection isn't one continuous range of text.
  • new CommentedText(comment, start, length) comments a range without selecting it. start is one-based: 1 is the first character.
  • textControl1.Comments.Add() without a parameter opens the input box for the user, as the ribbon button does.

Read and Answer Comments

The collection enumerates all comments and replies of the text part. GetItem returns the comment at the input position, GetItem(true) the next one and GetItem(false) the previous one, or null if there is none. The following code lists the comments with their replies and answers the comment at the input position:

private void listCommentsButton_Click(object sender, EventArgs e)
{
    System.Text.StringBuilder report = new System.Text.StringBuilder();

    foreach (CommentedText comment in textControl1.Comments)
    {
        // Replies are listed below the comment they answer
        if (comment.RepliedComment != null)
        {
            continue;
        }

        report.AppendLine($"\"{comment.Text}\": {comment.Comment} " +
            $"({comment.UserName}, {comment.CreationTime.ToLocalTime()})");

        foreach (CommentedText reply in comment.Replies ?? new CommentedText[0])
        {
            report.AppendLine($"    {reply.UserName}: {reply.Comment}");
        }
    }

    MessageBox.Show(report.ToString(), "Comments");
}

private void replyButton_Click(object sender, EventArgs e)
{
    CommentedText comment = textControl1.Comments.GetItem();

    if (comment != null)
    {
        textControl1.Comments.Add(new CommentedText("Done.", comment));
    }
}
  • Text is the commented text and Comment the comment, which can also be set.
  • UserName is the author and CreationTime the time in UTC.
  • Replies returns the replies, or null if there are none, and RepliedComment the comment a reply answers.
  • new CommentedText(comment, repliedComment) creates a reply, which Add inserts.

Edit, Remove and Navigate

A CommentedText has methods for the user interface and for its position:

MemberPurpose
EditCommentOpens the input box to edit the comment
ReplyToCommentOpens the input box for a reply; returns false for a comment that is itself a reply
ScrollToMoves the input position to the commented text and scrolls it into view
SelectSelects the commented text
Start, LengthThe one-based position and the length of the commented text
IDAn identifier of your own, for example the key of the comment in your database

CommentCollection.Remove removes a comment, and the text stays:

private void deleteCommentButton_Click(object sender, EventArgs e)
{
    CommentedText comment = textControl1.Comments.GetItem();

    if (comment != null)
    {
        textControl1.Comments.Remove(comment);
    }
}

Change the Highlighting

CommentHighlightMode of the TextControl decides when commented text is highlighted:

  • HighlightMode.Activated, the default, highlights a comment while the input position is in it.
  • HighlightMode.Always highlights all comments.
  • HighlightMode.Never highlights none.

A single comment can differ from this setting with its own HighlightMode and HighlightColor. Active set to false deactivates a comment: it is no longer highlighted, and GetItem skips it.

React to Comments

The following events inform your application about comments. Each handler receives CommentEventArgs, whose CommentedText is the comment:

EventOccurs when
CommentCreatedA piece of text has been commented
CommentDeletedA comment has been removed
CommentChangedThe text of a comment has been changed
CommentStateChangedA comment has become active or inactive
CommentedTextEntered, CommentedTextLeftThe input position has moved into or out of commented text

The following handler shows the comment at the input position in the title bar of the form. Connect it on the Events page of the Properties window, or with textControl1.CommentedTextEntered += textControl1_CommentedTextEntered; in the constructor:

private void textControl1_CommentedTextEntered(object sender, CommentEventArgs e)
{
    Text = $"{e.CommentedText.UserName}: {e.CommentedText.Comment}";
}

To remove the comments of every text part before a document is sent, see Comments and Tracked Changes. To read the comments of stored documents without the editor, for example to import them into a database, use the Comments collection of a ServerTextControl, as shown in the blog article Extracting Comments from DOCX Files in .NET C#.

Next Steps