Controls

RichTextBox

RichTextBox is the interactive host for structured rich documents. It owns focus, caret and selection state, editing commands, scrolling, rendering, hyperlink activation, and the control-template contract. The document tree it hosts is covered separately on the RichTextDocument page.

Mental model

The control

RichTextBox behaves like an editor surface: it receives input, manages selection and caret visibility, executes editing commands, and renders the live document viewport.

The model

The hosted document is still a structured tree of blocks and inlines. When you need to reason about paragraphs, runs, lists, tables, or hosted UI, jump to RichTextDocument.

Choose the layer deliberately. Reach for RichTextBox when your problem is about editing behavior or UI integration. Reach for RichTextDocument when your problem is about document structure or serialization.

Quick start

Minimal editor surface

<RichTextBox Width="520"
             Height="260"
             Padding="8"
             BorderThickness="1"
             TextWrapping="Wrap" />

Assign a document

var document = new FlowDocument();
var paragraph = new Paragraph();
paragraph.Inlines.Add(new Run("Edit me"));
document.Blocks.Add(paragraph);

editor.Document = document;

Read-only rich viewer

<RichTextBox Width="560"
             Height="280"
             IsReadOnly="True"
             TextWrapping="Wrap" />

What the control owns

  • Keyboard and pointer editing gestures, including word motion, selection extension, paragraph breaks, list commands, and hyperlink activation.
  • Caret and selection state through both integer offsets and document-aware pointer or range APIs.
  • Undo or redo orchestration through the document undo manager.
  • Viewport scrolling, caret visibility, auto-scroll during drag selection, and wheel handling.
  • Rendering of the document surface, selection geometry, caret, table lines, and hosted document children.

Inspect selection state

Console.WriteLine(editor.CaretIndex);
Console.WriteLine(editor.SelectionStart);
Console.WriteLine(editor.SelectionLength);

Hit test into the document

TextPointer? pointer = editor.GetPositionFromPoint(mousePosition, snapToText: true);
if (pointer.HasValue)
{
    var offset = DocumentPointers.GetDocumentOffset(pointer.Value);
    Console.WriteLine($"Hit offset: {offset}");
}

Working with the document

RichTextBox always hosts a valid document. Setting Document to null is coerced back to a default document, and whole-document operations are intentionally separate from selection-scoped operations.

Whole-document APIs

Use Document, Load, and Save when you want to replace or serialize the complete document.

Selection APIs

Use Select, LoadSelection, and SaveSelection when the operation should affect only the current range.

Whole-document load

using var stream = new MemoryStream(Encoding.UTF8.GetBytes(payload));
editor.Load(stream, "Xaml");

Selection-scoped load

using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Inserted text"));
editor.LoadSelection(stream, "Text");

Document structure is preserved by default. The editor will preserve rich fragments during selection edits, clipboard operations, and many command-driven transformations. The full model details live on the RichTextDocument guide.

Commands and shortcuts

Toolbar buttons, keyboard shortcuts, and explicit command execution all go through the same routed command surface.

Core editing

Backspace, delete, copy, cut, paste, undo, redo, select-all, paragraph breaks, line breaks, and word navigation.

Rich editing

Formatting toggles, list indent or outdent, table insertion, cell split or merge, document navigation, and hyperlink activation.

Formatting commands

CommandManager.Execute(EditingCommands.ToggleBold, null, editor);
CommandManager.Execute(EditingCommands.ToggleItalic, null, editor);
CommandManager.Execute(EditingCommands.ToggleUnderline, null, editor);

Structure commands

CommandManager.Execute(EditingCommands.ToggleBullets, null, editor);
CommandManager.Execute(EditingCommands.InsertTable, null, editor);
CommandManager.Execute(EditingCommands.SplitCell, null, editor);

Serialization and clipboard

The control supports both rich and plain formats. Rich formats preserve more structure; text formats flatten the document.

  • Rich formats include Flow XML, Xaml, XamlPackage, and Rich Text Format.
  • Plain formats include Text and UnicodeText.
  • SaveSelection and LoadSelection let you round-trip only the active fragment.

Save the whole document

using var stream = new MemoryStream();
editor.Save(stream, FlowDocumentSerializer.ClipboardFormat);

Save only the current selection

using var stream = new MemoryStream();
editor.SaveSelection(stream, "Xaml");

Layout, templating, and rendering

The control template provides outer chrome only. RichTextBox still owns the rich document viewport and rendering behavior.

Template contract

The default template must expose PART_ContentHost so the control aligns with the rest of the text-input family.

Control-owned surface

Selection, caret, rendered runs, table borders, and hosted document UI are still drawn and managed by the control itself.

Template example

<Style TargetType="{x:Type RichTextBox}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type RichTextBox}">
        <Border Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
          <ScrollViewer x:Name="PART_ContentHost"
                        Margin="{TemplateBinding Padding}"
                        Focusable="False" />
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Events and integration points

  • DocumentChanged fires when the document is replaced or mutates internally.
  • TextChanged fires for text-affecting edits.
  • SelectionChanged tracks caret and selection transitions.
  • HyperlinkNavigate bubbles the activated target URI.

Subscribe to the main events

editor.DocumentChanged += (_, _) => Console.WriteLine("Document changed");
editor.TextChanged += (_, _) => Console.WriteLine("Text changed");
editor.SelectionChanged += (_, _) => Console.WriteLine("Selection changed");
editor.HyperlinkNavigate += (_, args) => Console.WriteLine(args.NavigateUri);

When to use it

Use TextBox for plain text. Use RichTextBox when the UI problem includes rich selection, structured clipboard content, hyperlinks, lists, tables, or hosted UI inside the document.

Go deeper on the model. For blocks, inlines, tables, hosted UI containers, and example-rich document construction patterns, continue to RichTextDocument.