Overview
RichTextEditor is an Avalonia UserControl that owns a DocumentController
and a DocumentRenderer. It translates Avalonia pointer and keyboard events into Core API calls,
and renders the document to an Skia canvas using an anti-flicker caching strategy.
Use RichTextEditor directly when you want to supply your own toolbar, scrollbars, or embed the
editor inside a complex layout. Wire a DocumentToolBar to it by setting
Toolbar.Editor = myEditor.
AXAML Usage
<pe:RichTextEditor
x:Name="Canvas"
Width="800"
HorizontalAlignment="Left"
VerticalAlignment="Top"
/>
<!-- Pair with a toolbar -->
<pe:DocumentToolBar x:Name="Toolbar" />
// In OnInitialized() or OnLoaded()
Toolbar.Editor = Canvas;
Properties
| Property | Type | Description |
|---|---|---|
| DocumentController | DocumentController |
The underlying Core controller. Created during OnLoaded().
Use this to apply styles, navigate, export, or react to events.
This is a Direct Avalonia property — bindable.
|
| SelectionInfo | SelectionInfo | Current paragraph-level selection state (alignment, list type, list level, line spacing). Updated on every navigation event. Bindable Direct property. |
| ZoomLevel | double | Zoom multiplier (default: 1.0). Styled Avalonia property — can be set from AXAML or code. |
| DocumentHeight | float | Computed: DocumentController.DocumentHeight. The maximum vertical scroll extent. |
Events
Canvas.ContentSizeChanged = (info) =>
{
vertScrollBar.Maximum = info.Height;
vertScrollBar.ViewportSize = Canvas.Bounds.Height;
};
Methods
vertScrollBar.PropertyChanged += (_, e) =>
{
if (e.Property == ScrollBar.ValueProperty)
Canvas.ScrollTo(0, vertScrollBar.Value);
};
Input Handling (Internals)
RichTextEditor overrides these Avalonia virtual methods and bridges them to Core:
| Override | Delegates To |
|---|---|
OnPointerPressed() | DocumentController.Click(point) |
OnPointerMoved() (button down) | DocumentController.DragTo(point) |
OnPointerMoved() (no button) | DocumentController.HoverAt(point) |
OnPointerReleased() | DocumentController.PointerReleased(point) |
OnPointerWheelChanged() | DocumentController.ScrollBy(delta) |
OnKeyDown() | DocumentController.OnKeyEvent(keyInfo) |
OnTextInput() | DocumentController.Insert(text) |
OnGotFocus() | DocumentController.HasFocus = true |
OnLostFocus() | DocumentController.HasFocus = false |
Avalonia key codes are converted to Core KeyCode values via
AvaloniaExtensions.ToKeyCode() using a static lookup table.
Rendering
RichTextEditor overrides Render(DrawingContext) and creates a lightweight
DocumentRenderOp (a custom draw operation) each frame. The actual rendering is
delegated to DocumentRenderer, which maintains an SKPicture cache.
The cache is only re-recorded when the document has changed, preventing unnecessary redraws.
DocumentController.Invalidate() (e.g., during cursor blink or scroll) are debounced
via _redrawPending. Only one InvalidateVisual() is dispatched per Avalonia render frame.
Custom Layout Example
A minimal setup using RichTextEditor without WordProcessor:
// AXAML (abbreviated)
<DockPanel>
<pe:DocumentToolBar x:Name="Toolbar" DockPanel.Dock="Top" />
<ScrollBar x:Name="VScroll" DockPanel.Dock="Right" Orientation="Vertical" />
<pe:RichTextEditor x:Name="Canvas" />
</DockPanel>
// Code-behind
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
Toolbar.Editor = Canvas;
Canvas.ContentSizeChanged = (info) =>
{
VScroll.Maximum = info.Height;
};
VScroll.PropertyChanged += (_, args) =>
{
if (args.Property == ScrollBar.ValueProperty)
Canvas.ScrollTo(0, VScroll.Value);
};
}