Avalonia.Controls.Documents 12.4.0

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Avalonia.Controls.Documents --version 12.4.0
                    
NuGet\Install-Package Avalonia.Controls.Documents -Version 12.4.0
                    
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="Avalonia.Controls.Documents" Version="12.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Avalonia.Controls.Documents" Version="12.4.0" />
                    
Directory.Packages.props
<PackageReference Include="Avalonia.Controls.Documents" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Avalonia.Controls.Documents --version 12.4.0
                    
#r "nuget: Avalonia.Controls.Documents, 12.4.0"
                    
#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.
#:package Avalonia.Controls.Documents@12.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Avalonia.Controls.Documents&version=12.4.0
                    
Install as a Cake Addin
#tool nuget:?package=Avalonia.Controls.Documents&version=12.4.0
                    
Install as a Cake Tool

Avalonia.Controls.Documents

Core document model library for Avalonia rich text controls. Provides rope-based text storage, a hierarchical document tree, text pointers, undo/redo, and snapshotting.

Features

  • Rope-based text storage (RopeTextStore) with O(log n) insert and delete
  • Document tree model (TextDocument, TextDocumentNode) for structural editing
  • Text pointer API (TextPointer, TextRange) with generation-based invalidation
  • Undo/Redo (UndoManager) with structural snapshot support
  • FlowDocument model with blocks (paragraphs, lists, tables, sections) and inlines
  • Document snapshots for thread-safe background serialization

Quick Start

var document = new FlowDocument();
var paragraph = new Paragraph();
paragraph.Inlines.Add(new Run("Hello, world!"));
document.Blocks.Add(paragraph);

Documentation

See the documentation for more details.

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.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (7)

Showing the top 5 NuGet packages that depend on Avalonia.Controls.Documents:

Package Downloads
Avalonia.Controls.Markdown

Markdown rendering control for Avalonia applications

Avalonia.Controls.Documents.Serialization.Html

HTML serializer for Avalonia.Controls.Documents

Avalonia.Controls.RichTextEditor

Interactive rich text editor control for Avalonia with selection, formatting, and undo/redo support

Avalonia.Controls.Documents.Serialization.Rtf

RTF serializer for Avalonia.Controls.Documents with high-fidelity import and export support

Avalonia.Controls.Documents.Serialization.Docx

DOCX serializer for Avalonia.Controls.Documents based on Open XML

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
12.4.1 135 11/5/2026
12.4.0 12,881 10/28/2026
12.3.1 22,267 10/8/2026
12.3.0 8,139 9/11/2026
12.2.3 16,404 8/20/2026
12.2.2 35,987 7/31/2026
12.2.1 712 7/29/2026
12.2.0 10,310 7/23/2026
12.1.5 9,360 6/29/2026
12.1.4 11,240 6/22/2026
12.1.3 842 6/9/2026
12.1.2 858 6/2/2026
12.1.1 1,548 5/14/2026
12.1.0 3,883 5/12/2026

## 12.4.0

### Features
- Bookmarks: a text block can carry names that a link reaches through the fragment `#name`. `TextRange.AddBookmark` and `RemoveBookmark` change them with undo, `Block.Bookmarks` reads them, and `BlockSnapshotNode.Bookmarks` and `DocumentSnapshotBuilder.AddParagraphBookmark` carry them through snapshots. The names live in the text model, so they survive snapshots, undo, copy and paste with no element realized. A name stays with the first half when its block splits, moves to the surviving block on a merge, and goes with a block that is deleted whole; a pasted block whose name the document already carries arrives without it.
- In-document links navigate. A click on a `RichHyperlink` whose `NavigateUri` is a fragment (`#name`), when nothing handles `RequestNavigate`, scrolls every view (`FlowDocumentScrollViewer`, `FlowDocumentPageViewer`, `RichTextEditor`) to the block the name addresses and moves the caret there. In page layout the target's page shows from the top of its sheet, as `BringPageIntoView` places it, and scrolls further only when the target sits below the first screen of the page. The name is looked up as a bookmark, then as a fragment a block derives from its text (a markdown heading's slug), then as a footnote label; a link inside a footnote or page band also finds names in the main document. A fragment that names nothing is no longer handed to the platform launcher, which opened nothing.

### Behavioural changes
- A line break (`RichLineBreak`, Shift+Enter, a break read from DOCX, HTML or markdown) is a single line feed in the document text on every platform. It was `Environment.NewLine`, two characters on Windows, so `TextPointer.GetPositionAtOffset`, `CreatePointer(int)`, hit testing and selection could produce an offset between its CR and LF, and every edit had to widen or snap around that. `TextRange.Text`, `TextDocument.GetText` and `TextChangeEventArgs.InsertedText` now return `\n` for a break, and a break shifts the offsets after it by one instead of two on Windows. Plain-text export already wrote `\n` and is unchanged.
- `TextDocument.TextChanged` is raised once per operation, with the operation's net change: `[Offset, Offset + OldLength)` before it is `NewLength` characters after it. An operation is an outermost change scope (`BeginChange`, a keystroke, a paste, one undo or redo step), and `DocumentChangedEventArgs.TextChanges` carries the same single change. It was raised per internal edit, and only on the text paths: typing over a selection, Shift+Enter, `ReplaceText` and `RichRun.Text` raised several events, Enter reported a deletion of text it only moved, a paste of two paragraphs reported -2 characters, an undo reported part of its change and `Blocks.Remove` reported nothing. The region covers every character the operation wrote, without comparing the text to find what stayed the same. A paragraph boundary is not a character: splitting a paragraph (Enter) or joining adjacent ones (Backspace or Delete at the boundary) writes no text and raises no event, and neither does its undo or redo. Shift+Enter, or Enter in a preformatted block, inserts a line feed and reports an `Insert`. `InsertedText` is read from the document on first access instead of being copied for every event, and is no longer cut off at an internal text segment.
- `TextRange.ReplaceText` and `ReplaceTextAt` behave like typing over the range: blocks and inlines the range covers whole are removed (a footnote anchor takes its note along), and the partially covered blocks at either end join. They left a covered paragraph behind as an empty block and a covered table as a table without rows. The returned pointer is still at the start of the new text, and undo restores the range in one step.
- Setting `RichRun.Text` keeps the run in the document with its formatting. It emptied the run first, which removed it from the document (its `Text` then read back empty) and put the new text into the next run with that run's formatting. This holds for empty text too: a run set to `""` stays in its paragraph, and text set on it or inserted at its `ContentStart` later goes into it with its formatting. Setting `Text` raises one `Replace` change and undoes in one step, back into the same run.
- A deletion that removes or joins inlines (across two runs, over a whole run, over a line break or embedded object) undoes from a snapshot of its block, as structural edits do, so the inlines after undo are new element instances; undoing a paragraph merge likewise brings the merged-away paragraph back as a new `Paragraph`. A deletion inside one run keeps the run element across undo. Code that keeps element references across an undo has to look them up again.
- `TextPointer.InsertText` on a pointer whose position has left the document (its paragraph was replaced or removed) re-resolves the pointer by offset first. It wrote into the detached content, so the text never appeared.

### Fixes
- Plain-text export writes a markdown heading or code block (any block kind registered for a `Paragraph` subclass) as a paragraph. It recognised only the built-in paragraph kind and dropped these blocks with their text.
- Copying part of a list item, table cell or section no longer brings its container along. A range snapshot always captured from the top-level block down, so selecting one word of a bullet point copied a one-item list holding that word: pasting it added a new bullet, and the plain text put a bullet marker in front of the word. A selection that stays within one paragraph of a nested block now copies that paragraph alone, with its inline formatting, whether it covers a word or the item's whole text. A selection that covers the whole top-level container (Select-All over a one-item list, a section's content range) or spans several items still copies the container.
- A line break read from RTF (`\line`) or XAML (`<RichLineBreak />`) shows the text after it. These readers built a break with no character in the text (`LineBreakInlineDraft` without `PreserveTextContent`), the text source ended the paragraph at it, and the rest of the paragraph was never laid out; `GetText` and plain-text export ran the two lines together. `DocumentSnapshotBuilder` now gives every line break its line feed, and `LineBreakInlineDraft.PreserveTextContent` has no effect.
- Undoing a property set on a block (`TextAlignment`, `Foreground`, any inherited property) takes one step. Every inline that inherited the value recorded a step of its own, so the first undo seemed to do nothing, and undoing those steps set the old value on each inline locally, which stopped it from inheriting from its block afterwards.
- Undo restores the formatting of the text it brings back. Deleting across two plain runs came back as one run, and setting `RichRun.Text` on a bold run or replacing text across a formatted run came back as plain text in the neighbouring run.
- Undo and redo after an intervening structural undo edit the live document. A step that remembered a paragraph or table edited the copy the structural undo had replaced, while its text went to the live document, so the document's structure and its text disagreed. A property change (alignment, color, any element property) likewise undid and redid on the element it was made on, which a structural undo in between had replaced, so the block in the document kept its value. Undoing the removal of a page band brought the band back but bound its header and footer slots on the replaced section, so the section in the document showed none.
- Applying a hyperlink or inline formatting up to the middle of a run that had not been displayed yet (a run loaded from a file and scrolled out of view) covered the rest of the run too.
- Replacing all of a paragraph's text left the new text in the paragraph without a run to hold it.
- Deleting a table column that held a line break or a footnote anchor left those characters behind, so the document length no longer matched its content. The column's footnote anchors now take their notes along.
- Clearing selected table cells, or replacing text over a footnote anchor, removed the anchor but kept its numbered note.
- Custom paragraph kinds (a markdown heading, a task item's paragraph) take part in everything a paragraph does: formatting promotion and Clear Formatting, inline containment, footnote anchor repaint and table cell merges. Pasting paragraphs at the end of a heading merges the first one into it, pasting inside one splits it, and pasting at its start keeps the heading; a code block takes pasted paragraphs as lines.

### Performance
- Typing or streaming text at the end of a document collects small appends into one buffer instead of creating a text segment per character, and reading the text no longer flushes that buffer. 5000 one-character appends went from 550 us and 2.57 MB to 18 us and 11 KB.