Simplecto.Avalonia.RichTextBox 1.0.12

dotnet add package Simplecto.Avalonia.RichTextBox --version 1.0.12                
NuGet\Install-Package Simplecto.Avalonia.RichTextBox -Version 1.0.12                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Simplecto.Avalonia.RichTextBox" Version="1.0.12" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Simplecto.Avalonia.RichTextBox --version 1.0.12                
#r "nuget: Simplecto.Avalonia.RichTextBox, 1.0.12"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Simplecto.Avalonia.RichTextBox as a Cake Addin
#addin nuget:?package=Simplecto.Avalonia.RichTextBox&version=1.0.12

// Install Simplecto.Avalonia.RichTextBox as a Cake Tool
#tool nuget:?package=Simplecto.Avalonia.RichTextBox&version=1.0.12                

A RichTextBox control for Avalonia

NuGet version

As of 2024, Avalonia doesn't yet come with a RichTextBox, and since I needed one I created a "poor-man's version" based on the existing control SelectableTextBox.

Mirroring WPF, this RichTextBox control uses the concept of a FlowDocument (FlowDoc), which contains Blocks (at the current time, only Paragraph is available, although Section or Table could be added later). Paragraph contains IEditable objects (EditableRun (from Avalonia.Run) and EditableInlineUIContainer (from Avalonia.InlineUIContainer)) and it is bound to an EditableParagraph (inheriting from SelectableTextBlock).

The FlowDoc is at heart merely an ObservableCollection of Blocks bound as the ItemsSource of an ItemsControl inside a ScrollViewer. Upon adding the appropriate key input handling, voila, a RichTextBox magically appeared.

(The hard part after that was implementing the selection logic, because Selection for the RichTextBox has to be able to move between and span multiple Paragraphs (SelectableTextBlocks), both with the keyboard and the mouse, and to allow editing functions that involve splitting or merging Paragraphs. And of course the Inline logic for spanning, inserting, splitting or deleting Inlines.

classDiagram
    class RichTextBox{
        +FlowDocument FlowDoc
    }
    class FlowDocument{
        +ObservableCollection<Blocks>
    }
    class FlowDoc{
        -List<TextRange> TextRanges
    }
    class Blocks{
        +Paragraph Paragraph
    }
    class Paragraph{
        +IEditable Objects
        +EditableParagraph EditableParagraph
    }
    class IEditable{
        +EditableRun EditableRun
        +EditableInlineUIContainer EditableInlineUIContainer
    }
    class Selection{
    }
    class TextRange{
        +int Start
        +int End
        +int Length
        +Delete()
        +string Text
        +ApplyFormatting(AvaloniaProperty,  object)
    }

    RichTextBox --> FlowDocument : has
    FlowDocument --> Blocks : has
    FlowDoc --> TextRange : has
    Blocks --> Paragraph : contains
    Paragraph --> IEditable : has
    TextRange --> Selection : instance
    RichTextBox --> FlowDoc : has

Currently, when used in Debug Mode, the RichTextBox displays Inline debugging information in a right-hand panel - Inline starts, paragraph starts, inline texts, and indicates the inlines of the Selection start and end by background color coding. The Debugger panel is not shown in Release mode.

The RichTextBox has the usual key functions:

  • <kbd>Ctrl</kbd>+<kbd>B</kbd> for bold/unbold
  • <kbd>Ctrl</kbd>-<kbd>I</kbd> for italic/unitalic
  • <kbd>Ctrl</kbd>-<kbd>U</kbd> for <u>underline</u>/remove underline
  • <kbd>Ctrl</kbd>-<kbd>Z</kbd> for undo
  • <kbd>Ctrl</kbd>-<kbd>A</kbd> for select all

The FlowDoc has a Selection property, with Start, End, Length, Select, Delete, Text, etc.

The RichTextBox also includes the concept of TextRange (of which Selection is merely a special case), which can be defined to format text from code independent from the current FlowDoc.Selection. A new TextRange is created with a Start and End (and its owning FlowDoc), whereby it is automatically added to the FlowDoc's TextRanges List so its Start and/or End can be updated whenever text changes in the FlowDoc require it. TextRange also has an ApplyFormatting property which allows any AvaloniaProperty to be applied that pertains to Inlines.

I've tried to add Undos for all editing possibilities, but Undo hasn't really been stress-tested to the max, yet. There is no particular limit set for number of undos at the current time. (Redo doesn't exist yet.)

The RichTextBox content can be saved/loaded either as straight Xaml or a XamlPackage (to preserve images), similar to the WPF RichTextBox. It can also save and load the FlowDoc content as a Word document (.docx), though only with a subset of Word document features. This includes text, some common text/paragraph formatting, and most images, but not very much else at this time.

Various future to-do improvements include:

  • Finish paragraph formatting (such as line spacing)
  • RTF export (RTF import was recently added, but unfortunately the best RTF parser I could find (RtfDomParser) is only an RTF reader, without a corresponding RTF code generator)
  • Save/Load Xaml (to/from a stream) for Selection and any given TextRange
  • Adding Table and Section Block types
  • Allow the Undo limit to be set
  • Redo functionality (could be a headache)
  • More stress testing
  • A quirk or two at times when extending selection using <kbd>PageUp</kbd> or <kbd>PageDown</kbd> Key.

RtfDomParser can be found at https://github.com/SourceCodeBackup/RtfDomParser, but for this project I had to manually modify it to use Avalonia.Media instead of System.Drawing

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Simplecto.Avalonia.RichTextBox:

Package Downloads
BestChat.Platform.UI.Desktop

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.12 225 8/28/2024
1.0.10 104 8/28/2024

In version 1.0.12, fixed an inline range-calculating error - cloned inlines need to retain their paragraph-relative text positions, and right-docked Debugger panel is not visible (removed) by default (developers see github link for using Debugger Panel).