Avalonia.Controls.Documents 12.3.0

Prefix Reserved
dotnet add package Avalonia.Controls.Documents --version 12.3.0
                    
NuGet\Install-Package Avalonia.Controls.Documents -Version 12.3.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.3.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.3.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.3.0
                    
#r "nuget: Avalonia.Controls.Documents, 12.3.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.3.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.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Avalonia.Controls.Documents&version=12.3.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.3.0 799 9/11/2026
12.2.3 10,566 8/20/2026
12.2.2 24,147 7/31/2026
12.2.1 625 7/29/2026
12.2.0 10,208 7/23/2026
12.1.5 9,166 6/29/2026
12.1.4 8,853 6/22/2026
12.1.3 760 6/9/2026
12.1.2 816 6/2/2026
12.1.1 1,310 5/14/2026
12.1.0 2,628 5/12/2026

## 12.3.0

Adds to 12.2.3 and removes nothing: every type and member a 12.2.3 build could reference is still there with the same signature, checked against the released packages on every build.

### Features
- `TextPointer.Clone()` is public. The cached accessors `TextDocument.ContentStart`/`ContentEnd` and `RichTextElement.ContentStart`/`ContentEnd` all document a clone as mandatory before the pointer is stored, and all four link it; until now an external caller who followed that advice could not compile it.
- `SelectionSnapshot.Capture(TextSelection)` builds the snapshot that `UndoManager.BeginUndoUnit` and `IUndoScope.SetSelectionAfter` take. The type appeared in both signatures with no public way to make one.
- `FlowDocumentScrollViewer` implements the `IInteractiveTextHost` surface implicitly: `ContentChanged`, `DocumentChanged`, `TextDocument` and `UIScope` no longer need a cast, and the two events are raised rather than being no-ops.
- `FlowDocumentPageViewer` forwards `IsPaginationComplete`, `PageBackground` and `PageBorderBrush` from the view it hosts, so "page N of M" can tell a provisional M from a final one and a sheet's colour is set on the control the consumer placed.
- `BlockAdornerCollection` implements `IReadOnlyList<BlockAdorner>`, so adorners can be enumerated and registered from XAML content syntax.
- The distance from the sheet edge to a page band belongs to the document: `TextDocument.PageBandDistance` (with `ResolvePageBandDistance()`), carried by `DocumentFormatting.PageBandDistance` and mirrored by the new `FlowDocument.PageBandDistance`. `PagedTextView.PageBandDistance` and `RichTextEditor.PageBandDistance` keep working and are now handles on it: setting one writes through to the document, and the view follows a distance the document brings with it. The paged view, printing and the PDF export all resolve the same value.
- `Block.TextIndent` and `Block.TabStopPositions` (and the same two on `ListItem`) give the model's `BlockFormatting.TextIndent` and `TabStopPositions` a home on the element. First-line indents and tab stops read from DOCX, RTF and XAML now survive realization instead of vanishing the moment a block renders.
- `BlockFormatting.CornerRadius` carries `Block.CornerRadius` and `ListItem.CornerRadius` through a snapshot, so a clone and an undo of a block split keep rounded corners. No file format carries it.
- `TextPageBand.Name` keeps the name a format gave a band, so loading and saving XAML no longer renames every band.
- `DocumentSnapshotBuilder.SetDocumentTextElementFormatting` authors the document's default font, size and colours model-first; `TextDocument.DefaultTextFormatting` carries them, and `DocumentSnapshot.CaptureListItemFormatting` captures a realized `ListItem`'s block formatting (a `ListItem` is not a `Block`, and nothing captured it before).
- `PageBandReferences.Slots` is the table of the four reference slots, next to the indexer that consumes it.
- A note can be cited more than once: `RichFootnoteCitation` (`NoteId`) is a further citation of a note whose `RichFootnoteReference` anchor is elsewhere - the same note cited again, or cited from inside another note - on the new built-in `TextDocumentNodeKind.FootnoteCitation`, authored with `DocumentSnapshotBuilder.AddFootnoteCitation(noteId)` or a `FootnoteCitationInlineDraft` and snapshotted as a `FootnoteReferenceSnapshotNode` of that kind. Like the anchor it is an atom of one object replacement character that stores no number: both text sources and the PDF layout paint the position of its note among the owning document's notes, so a note added or removed renumbers every citation of it and no digit is ever document text. It owns no note - deleting it leaves the note and the numbering alone - and unlike an anchor it is legal inside a note body, where it numbers against the document that owns the notes. A range copied without the anchor carries no note, so a citation pasted on its own is dropped rather than pointing at nothing.
- Footnote numbering formats, and a note number drawn like a list marker. `FootnoteNumberFormat` (`Decimal`, `LowerRoman`, `UpperRoman`, `LowerLatin`, `UpperLatin`, `Symbols`: the traditional asterisk, dagger, double dagger, section sign, double vertical line and pilcrow sequence, doubled past the sixth note) names how a document numbers its footnotes, document-wide and continuous in anchor order. The text document owns it (`TextDocument.FootnoteNumberFormat`): the anchors store no number, so setting the format is one undo unit of its own and raises `FootnotesChanged`, from which the views re-render every anchor and note. `FlowDocument.FootnoteNumberFormat` mirrors it (a value set before the text document exists applies when it is created; a nested document ignores it), `DocumentFormatting.FootnoteNumberFormat` carries it in snapshots (`DocumentSnapshotBuilder.SetFootnoteNumberFormat`), so `Clone`, `FromSnapshot` and XAML, DOCX and RTF round-trip it, and an anchor pasted from a document with another format takes the target's. The number a note container shows follows the list marker rule: it takes family, size and color from the note's first run (else the first block, else the note element, which inherits the owner's font) and drops the run's decorations, at the scale a superscript run gets, sitting at the top of the note's first line like the anchor in the body, its ink a quarter em before the content; decimal numbers use tabular figures. Every note of a document shares one number strip, at least 1.5 em of the owning document's root font and wide enough for the last ordinal (`NestedDocumentNode.StripWidth`), computed alike by the views and the PDF export. A theme setter on `Footnote` (`FontSize`, `Foreground`) therefore restyles a note's text and its number together.
- Page headers and footers as nested documents. The text model owns them: `TextDocument.PageBands` holds `TextPageBand`s (a nested `TextDocument` with role, rule and identity), snapshots and restores them with the document, joins their undo to its own, raises `PageBandsChanged`, and `PageBandPolicy` resolves a page's band from the nodes without realizing an element, so a `FlowDocument` is optional here as everywhere. A band's text document has a root of kind `PageBand`: `NodeKindFlags.Root` marks the root kinds, `TextDocumentNodeKind.IsRoot` is what navigation and structure code ask instead of comparing against `Document`, and what a document is travels with its tree and its snapshots (`FlowDocument.RootKind`, `TextDocument(TextDocumentNodeKind)`, `DocumentSnapshotBuilder.Create(rootKind)`). The element facade: a `PageBand` is a `FlowDocument` over a text-model band, materialized on demand by the owning document's `FlowDocument.PageBands` (its owner is its logical parent, undo joins the owner's stack, removing it clears every reference; bands added before the document's text document exists wait as pending like the blocks, so a document populated from markup stays lazy whatever the element order), and a change to `Role` or `Rule` on either side reaches the other. A band's `Role` (header or footer) and `Rule` place it by itself: `Default` is the running band on every page nothing else claims, `FirstPage` the document's first page, `EvenPage` the even pages (its presence is what turns facing pages on; a role without an even band keeps its running band there), `None` only where a section references it; the first band with a role and rule wins. A top-level `Section` references bands explicitly through `Header` and `Footer` (its running bands, the odd side under facing pages) and `FirstPageHeader` and `FirstPageFooter` (its first page; setting one is what makes that page special), whatever the band's rule, so one band can serve any number of sections; setting an unowned band adopts it, a band owned elsewhere is refused. There are no switches. `PageBandPolicy` is the shared rule the paged view and the PDF export both apply: a section's first-page reference on its first page, else the first-page band on page one, else the even band on even pages, else the section's running reference, else the document's default band; what resolves to nothing shows nothing. A band has its own buffer, selection and caret: a body selection never reaches into a band, bands never host footnotes or bands. Snapshots carry the bands as nested snapshots with role and rule (`DocumentSnapshot.PageBands`) and a section's references by band identity (`PageBand.Id`, carried in `PageBandReferences`), so `Clone`, `FromSnapshot`, structural undo and XAML, DOCX and RTF round-trip them; a section pasted from another document arrives without references. `FlowDocument.PageBandsChanged` reports pool and rule changes, and the paged view (`PagedTextView`) hosts the resolved band on every sheet through `PageBandHostFactory` (a read-only `InteractiveTextView` by default; the editor supplies editing surfaces), one `PageBandDistance` inside the margin and clipped to it; continuous views show the body only, plain text never sees bands.
- Page bands push the body, Word's rule: where a header or footer plus the band distance outgrows its margin, the page's body starts below the header or ends above the footer instead of the band clipping to the margin, in the paged view, print and the PDF export alike. Pages of one section can therefore differ in body height (a taller first-page or even-page band), and a band edit that changes the band's height paginates again. Before, a margin no larger than the band distance made the band vanish.
- Paged layout keeps its pages on screen while an edit repaginates. An edit used to take the break table apart (whole, for an edit on page one or in a footnote) and rebuild it in an idle slice, so the frames in between drew no sheets, recreated every band host whenever the page count moved and dropped the note regions with their bookings: the page decorations flickered on every keystroke. Now an edit only marks where the fill restarts, the table is overwritten in place from there (a stale tail stays visible until the fill reaches it and is trimmed when it completes), one slice runs inside the measure pass so the layout after a keystroke already sees the refilled pages, band hosts stay and refresh their page-number fields through their page context, and note region nodes stay with their bookings. Only a new document or new paper takes the table apart.
- `RichPageNumberField`: a page-number field inline (`Kind` = `CurrentPage` or `PageCount`, Word's PAGE/NUMPAGES). The number depends on pagination rather than on the document — one header band renders on every page with a different one — so nothing numeric is stored: the field occupies a single object replacement character, the convention an embedded object uses, and rendering resolves it against the page it lands on. The paged view and the PDF export resolve it per page; layout with no pages at all reads as a single page, and plain text exports the field as nothing, as it does any embedded object. The number is formatted by the conversion list markers and footnote numbers already share, so the field's length never follows the digits it shows. The field edits as one atomic unit; deleting it takes the structural undo path so its kind survives undo.
- Footnotes as nested documents, the shape page bands have. `RichFootnoteReference` is an atomic inline anchor paired by `NoteId` with a `Footnote`. Like `RichPageNumberField` it stores nothing numeric: the anchor occupies a single object replacement character, and rendering resolves its number from its position - the 1-based index of its note among the document's notes, which the document keeps in anchor order (re-sorting them when a structural edit moves anchors, on paste and on a snapshot restore). `FootnoteReferenceRun` paints the number in the document's `FootnoteNumberFormat` at the superscript scale on the superscript baseline, in the live views and the PDF export alike; plain text exports the anchor as nothing, as it does any embedded object. The note is a `Footnote`: a `FlowDocument` over a text-model note (`TextFootnote`, a nested `TextDocument` with a root of kind `Footnote`, the id and the optional `Label`), owned by the text document (`TextDocument.Footnotes`, in anchor order, raising `FootnotesChanged`) and materialized on demand by the owning document's `FlowDocument.Footnotes`, its owner its logical parent, its undo joined to the owner's stack, pending like a block until the owner's text document exists. The body is one document: `Blocks`, the tree, `ContentEnd`, `TextDocument.Length` and every range cover the body alone, a selection never reaches from the body into a note, and a nested document owns no notes or bands. The anchor owns its note through the element lifecycle: adding a `RichFootnoteReference` to a document (directly or inside a larger subtree) creates its paired empty `Footnote`, removing the anchor through an element collection takes the note with it (parked on the detached anchor, the same content-preserving contract a detached run has for its text), and re-attaching the anchor restores the pair, in the same document or another, so moving an anchor moves its note. Reach the paired note with `FlowDocument.FindFootnote(anchor)`. `Label` is the note's name in formats that address notes by name (markdown's `[^label]`); it is never displayed, and a note created in the editor has none. `TextRange.InsertFootnote()` is the editing verb (replace-selection semantics like `InsertImage`, accepts a caller-created anchor, returns the note, one undo unit). Deleting an anchor through text editing removes its note and renumbers the survivors in the same undo transaction, and note membership is undoable like band membership, so undo brings the same note back with its content; the schema refuses anchors inside page bands and notes. Snapshots carry the notes as nested snapshots (`DocumentSnapshot.Footnotes`, `FootnoteSnapshot`, built with `DocumentSnapshotBuilder.AddFootnote(noteId, content, label)` over a builder of its own), so `Clone`, `FromSnapshot`, structural undo and XAML, DOCX, RTF and markdown round-trip them; a range snapshot carries the notes its anchors cite, so a copied anchor pastes with its note (under a fresh id when the target already uses its own), and an anchor arriving without a note is dropped instead of becoming a numbered ghost (as is every anchor pasted into a band or a note body, which never host anchors). The views show the notes in `NestedDocumentNode` containers from the placement pool, each drawing its number in the strip its content is inset by: continuous views as a region below the last block above a separator rule, whose height rides in the layout padding like the band regions' (it shows whenever the document has notes); paged layout and print in a per-page region at the bottom of the page their anchor lands on, Word-style: a line carrying anchors reserves its notes' heights above the page bottom (plus a separator rule once per page with notes), a line that no longer fits the reduced height moves to the next page together with its notes, and editing a note reflows the pages that book it. The containers are live content: clicking into a note places the caret in the note's document, and `EnsurePositionVisible` reaches a note on an off-screen page or at the end of the flow.
- Per-section page setup: `Section` gains `PageWidth`, `PageHeight`, and `PagePadding`. They are the document's own property definitions (`AddOwner`) and inherit through the element tree, so an undeclared value reads the document's (or an enclosing section's); a section that declares any of them is a page-geometry section — in paginated output it starts on a new page (entering AND leaving it are section-break edges, which also sever keep-with-next chains like explicit breaks) and its pages use its own paper, margins, and content width (a landscape section between portrait ones, Word's section-break model). The paged view renders each sheet at its page's size, centered on its own width, with content measured and wrapped at that section's content width; page navigation, the current-page tracker, and scroll geometry follow the variable page stack, and the fit zoom modes resolve against the CURRENT page's paper — scrolling into a landscape section re-fits to the wide sheet. Printing composes each page on its own paper (`PrintPageEventArgs` reports it per page), the PDF export emits per-page MediaBoxes, and both engines break the same document on identical boundaries. The full serializer round-trip ships with it: a `SectionSnapshotNode` carries the `SectionPageSetup`, the snapshot builder takes it at `StartSection` or via `SetSectionPageSetup`, and range snapshots and `Clone` preserve it. Sections without page setup stay pure grouping containers with no page semantics, a page band pins them to their defaults so it never inherits the owner's paper, and an explicit view-level `PageSize`/`PageMargins` remains the uniform-paper override (the section still starts its page).
- Keep rules: `Block.KeepTogether`, `Block.KeepWithNext`, and `Paragraph.WidowControl` (with matching `BlockFormatting` fields) are honored by paged layout and PDF export through one shared cut-decision policy, so both paginate identically by construction. Keep-together avoids splitting the block across pages (best effort - a block taller than a page still splits); keep-with-next keeps the block's end on the same page as the next block's start, consecutive flagged blocks moving as one chain that gives up when it exceeds a page; widow/orphan control keeps at least two lines of a paragraph on each side of a page break, so a three-line paragraph never splits. Explicit breaks win: `BreakPageBefore` severs a keep-with-next bond and forced cuts are never keep-adjusted. The continuous on-screen flow ignores all three.
- `PageSizes`: well-known paper sizes and page-unit conversions in device-independent pixels — `A3`/`A4`/`A5`/`Letter`/`Legal`/`Tabloid`, `FromMillimeters`/`FromInches`, scalar `Millimeters`/`Inches` (for margins), and `Landscape`/`Portrait`. Assign to the paged viewers' and editor's `PageSize`, feed `FlowDocument.PageWidth`/`PageHeight`, or use with PDF export options; the built-in A4/2 cm fallbacks derive from the same values. Named sizes avoid the classic unit trap: US Letter is 612 × 792 in points but 816 × 1056 in device-independent pixels.
- `FlowDocumentPageViewer`: a paged document viewer (Word print layout) that renders the document as a vertical stack of page sheets. Page size and margins come from the control, the document's fixed page dimensions/padding, or A4 with 2 cm margins; explicit page breaks and `Block.BreakPageBefore` force new pages. Pagination runs at idle priority over the live layout, so `PageCount` grows progressively and repaginates on edits; `CurrentPageNumber`, `GoToPage`, and page navigation are bindable (pair with `PipsPager` for pager UI). Backed by the new `PagedTextView`, which answers the block-anchored virtualization's placement seams from a break table - selection and copy work as in `FlowDocumentScrollViewer`.
- Paged layout splits blocks across pages, like Word: pages fill line by line, a line that does not fit starts the next page exactly at its content top (the spacing above it is swallowed at the page top, the word-processor rule), lists and sections split between and inside their children, and a line taller than a page overflows its own page. Tables split between rows - a row is never cut through, rows covered by a row-spanning cell move to the next page together with their anchor row, and the grid closes the fragment above the page break and reopens below it (no gridline, row fill, or table background paints across the gap). The element keeps ONE visual whose internal positions jump across the page gap, so editing, caret placement, selection, and hit-testing work mid-fragment - clicking the gap between two pages of a paragraph puts the caret at the end of the line before it. The screen pagination and the PDF exporter's make identical line- and row-level break decisions.
- Explicit page breaks are split instructions, not content: Ctrl+Enter at a block boundary sets `Block.BreakPageBefore` on the following block, and mid-content it splits the paragraph exactly like Enter and flags the remainder, in one undo step. Deleting a break is the ordinary paragraph merge (the flag travels away with the merged block); when the neighbour cannot merge, Backspace clears the flag alone. There is no page-break element - one construct, `BreakPageBefore`, drives the paged view, the PDF export, and every serializer. The plain-text serializer writes a form feed as the flagged block's separator and maps form feeds back to the flag on read.
- The continuous views mark blocks carrying `BreakPageBefore` with a dashed rule across the block's top edge, Word draft-view style. `TextViewBase.PageBreakMarkerBrush` (themed via `DocumentPageBreakMarkerBrush`) colors the rule and is paint-only - it never affects layout. The paged view shows no marker: there the request manifests as the page transition itself.
- Paged layout zoom: `Zoom` (with `MinZoom`/`MaxZoom`), `ZoomMode` (`Custom`, `FitWidth`, `FitPage` - the fit modes track the viewport), and the read-only `EffectiveZoom` on `FlowDocumentPageViewer` and `PagedTextView`; Ctrl+wheel over the pages zooms about the pointer and switches back to custom, and an explicit zoom change keeps the viewport center. Zoom is a pure visual scale on the page sheets: text never re-wraps, pagination is untouched, and caret, selection, and hit-testing stay exact at every factor - what you see remains what prints. Works in the editor's page layout mode too.
- `Block.BreakPageBefore` (with `BlockFormatting.PageBreakBefore`): explicit page breaks are now part of the document model instead of living only in per-format round-trip metadata. RTF (`\pagebb`) and DOCX (`w:pageBreakBefore`, including style-defined breaks) map into it on read and emit from it on write, and paginated export (the new PDF serializer) honors it. The continuous on-screen flow ignores it.
- `IDocumentSerializer.CanRead`/`CanWrite` default interface members so serializer lists can be filtered without catching `NotSupportedException` (PDF is write-only; HTML is read-only). Markdown reads and writes, so it reports both and appears as a save format.
- Hyperlink titles round-trip through snapshots: `HyperlinkInlineDraft.Title` and `HyperlinkSnapshotNode.HyperlinkTitle` carry the optional title (markdown `[text](url "title")`, the HTML `title` attribute), surfaced on `RichHyperlink` as its string `ToolTip`; hyperlink drafts now also transfer `FormatMetadata` and `CustomData` like every other inline. `DocumentSnapshotBuilder.SetRowCustomData` sets extension data on the current table row (the markdown header-row flag).
- Nested documents in the one view. A `NestedDocumentNode` presents a page band's blocks on a sheet as an item of the paged view's own content host, one node per (page, document) placement over the shared elements, realized while the sheet is in view and recycled when it leaves; the footnote regions share that placement pool. Positions, hit tests, caret geometry, line navigation and `EnsurePositionVisible` route by the pointer's document to the placement the caret is in (the last hit test's, else the one nearest the viewport), a page-number field resolves through its node's `PageContext`, the band heights that set the body insets are measured on the same nodes, and an image in a band shown on several sheets is hosted once and drawn from its bitmap elsewhere. `IInteractiveTextHost.ActiveDocument` and `ActivateDocument(document, position)` (defaults: none, declined) let a host edit a nested document: the mouse activates the document under a press and never drags across documents, Escape returns to the main document, and `ToolbarTargetAreas.PageBand` names the band area. `PagedTextView.PageBandHostFactory`, the per-band `InteractiveTextView` hosts and the `page-band` class are gone. `DocumentNode.Element` is nullable: it is the node's `RichTextElement` as before, and null for the nested-document container, which presents a `FlowDocument` (`NestedDocumentNode.Document`) instead of an element.
- `TextViewBase.ShowPageBands` shows the document's running header above the first block and its running footer below the last as regions of the continuous flow: the same nested-document containers the sheets show, realized while their edge of the flow is on screen, their heights riding in the layout padding so the extent and the scroll anchoring see them while the block indices stay the root children's. First-page and even-page bands and a section's own bands stay page-layout only, a page-number field reads as a single page there, and `EnsurePositionVisible` reaches a region at either end. Off by default; the paged view ignores it.
- Band membership is undoable: a band joining or leaving `TextDocument.PageBands` (through the model or the `FlowDocument.PageBands` facade) records a unit when the owner has an undo manager, so undo of a removal brings the same band back at its index with the section slots that referenced it, and undo of an add removes it again. `TextRange.InsertPageNumberField(kind)` replaces the range with a page-number or page-count field in one undo unit, refused inside a footnote body.
- The read-only viewers select in page bands and footnotes. `FlowDocumentScrollViewer` (and so `FlowDocumentPageViewer`) answers `ActivateDocument` the way the editor does: a press into a note region or a band on a sheet moves the selection into that document (`ActiveDocument`, `ActiveDocumentChanged`, `ActivateDocument(document, position)`), Escape or a press into the body brings it back to where the body's selection was, each document keeps its own selection while it belongs to the body, and `SelectAll`, `CanCopy` and `Copy` follow the active one. A removed band or note drops its selection, a document swap lands on the new body, and with `IsSelectionEnabled` off nothing is enterable. Before, the viewers kept the interface's default and a press into a note or a band was dropped, so their text could not be selected or copied.
- `UnderlineStyle` gives the model the underline variant it had nowhere to keep: `None`, `Single`, `Double`, `Dotted`, `Dashed`, `DashDot`, `DashDotDot`, `Thick`, `Wave`, `Word`. It rides on `InlineFormatting.UnderlineStyle` and mirrors onto the facade as the inherited attached property `RichInline.UnderlineStyle`, so it survives a snapshot round trip with nothing realized, an element realization and a re-capture; before, the variant lived only on a serializer's round-trip metadata, which realization discards, and applying any formatting to a double-underlined run degraded it to a plain underline. `TextDecorationFlags.Underline` remains the switch that turns an underline on and the style only says which line to draw, so a style with no underline flag draws nothing and every existing document behaves as before. Null means unset and draws as `Single`, staying distinct from an explicit `Single` so that merging formatting down an inline chain keeps working by `child ?? parent`. Rendering expresses the variants through stroke thickness, dash pattern and offset: `Wave` is approximated by a round-capped short-dash line and `Word` (a scope, not a line style) draws as `Single`, and both round-trip through the model regardless. Clearing formatting clears the style, and two spans that differ only in underline style no longer merge into one.
- `TextDocument.PageSetup`, a `DocumentFormatting` carrying the document's page geometry through a snapshot. The five page-geometry properties are a facade over it.
- `TextDocumentNodeKind.Register<TElement>(string, NodeKindFlags)` and `Register<TElement>(string, NodeKindFlags, INodeKindHandler)`, which cannot be handed a type that is not a `RichTextElement`.
- `RichTextElementCollection<T>.FirstOrNull()` and `LastOrNull()`, which cannot be taken for the LINQ extensions that are always in scope beside them.
- `ClipboardOperations.CopyAsync` and `CutAsync` take a trailing `CancellationToken`.
- `IInteractiveTextHost.ActiveDocumentChanged` is raised when the selection enters or leaves a page band or a footnote. It has a default implementation, so an existing implementer is unaffected.
- `TextViewBase.ShowPageBreakMarkers` (with `ShowPageBreakMarkersProperty`) suppresses the dashed page-break rule without clearing its brush, so `PagedTextView` can hide it without disturbing the theme.
- `BlockAdorner.ZIndex` is backed by `BlockAdorner.ZIndexProperty`, so it can be set from a style, data-bound and changed at runtime. It stays virtual, so an existing override still works.
- `SnapshotNode.GetFormatMetadata<T>()` and `SetFormatMetadata<T>(T?)`, which `InlineDraft` now has in full.
- `TableColumn.Width` is backed by `TableColumn.WidthProperty`.

### Behavioural changes
- `TextDocument.ContentRange` and `FlowDocument.ContentRange` return a fresh `TextRange` on every read. They returned one cached instance, so `doc.ContentRange.DeleteCurrentBlock()` collapsed the document's own cached range onto the deletion point and every later read at that generation returned the collapsed range. Two reads now compare equal but are not the same object; nothing in the model relied on that identity.
- `FlowDocument.LoadAsync` parses on the thread pool and then builds the element tree through an explicit `Dispatcher.UIThread.InvokeAsync`. A `FlowDocument` and its elements belong to the dispatcher of the thread that constructed them, so a document assembled on a pool thread threw on the first property read from the UI thread; the dispatch no longer depends on an ambient `SynchronizationContext`, which a console host or a caller already off the UI thread does not have. To load with no UI thread involved, read a `DocumentSnapshot` with the serializer and materialize it with `TextDocument.FromSnapshot`, which has no thread affinity.
- Every serializer's `SerializeAsync` and `DeserializeAsync` run the body on the thread pool. They ran the whole write or parse inline and returned `Task.CompletedTask`, so awaiting one on the UI thread froze the UI for the full duration of a large export.
- Writing `TextDocument.PagePadding`, `PageWidth`, `IsPageWidthFixed`, `PageHeight` or `IsPageHeightFixed` records an undo unit and marks the change scope. They were plain auto-properties, so a page size set through them was invisible to undo, to the snapshot and to every serializer.
- `HighlightLayerCollection.Add` rejects a layer whose `Name` is already present. `GetLayer` returned the first of a duplicate pair and the other was unreachable.

### Fixes
- Deleting a selection that spans blocks left every footnote in it behind: the note kept rendering with an ordinal and was written into the saved file with no reference. Backspace over a multi-paragraph selection, typing over one and pasting over one all did it. Every deletion path now cascades the notes of the anchors it consumes.
- A footnote anchor or a page-number field inside a bold run, an italic or a hyperlink was deleted on the light text path: its note was orphaned, and undo brought the object replacement character back as ordinary run text - selectable content pointing at nothing, and a field with no kind. The scan that decides whether a deletion needs a structural scope now walks the whole deleted subtree instead of one level.
- Adding or removing a page band or a footnote, reordering notes and changing `FootnoteNumberFormat` recorded undo units and changed the saved file, but raised no `Changed` (and so no `RichTextEditor.ContentChanged`): a host enabling its Save button on that event left it disabled. They now raise it.
- Typing one character no longer drops another paragraph's borders, tab stops and exact spacings: a node that shifted without realizing kept its serializer round-trip metadata only on the identity fast path, and lost it on re-capture otherwise.
- A realized `ListItem` lost its margin, padding, alignment and indents on re-capture: a `ListItem` is not a `Block`, and the capture had no branch for it.
- A section's explicit all-zero `PagePadding` read as undeclared, so a full-bleed section printed and exported with the document's margins.
- Removing a page band realized every root-level block to clear the references to it, discarding the deferred snapshot each was holding - a `PageBands.RemoveAt(0)` on a freshly loaded document stripped the round-trip metadata from its whole body. The references are cleared through the nodes, realizing nothing, and a still-deferred section's reference is cleared too.
- Pagination realized every root `Section` to read its page geometry. It reads `SectionPageSetup` from the node instead, the way the band references are already read.
- Printing reserved the body inset for a header or footer and drew neither, so a printed page started one header-height lower than an unheadered one with the header area blank. It draws the document's bands now.
- A band whose height follows the page number or the page count was measured once and that height used for every page, so the view and the PDF export disagreed from the page where the number wraps. The height is cached per page, and the re-measure after a band edit uses each entry's own page instead of page one.
- `PageZoomMode.FitPage` sized the sheet to the viewport height alone, but the sheet stack keeps one `PageGap` of surround above the first sheet, so the bottom of a "fitted" page fell outside the viewport. The fit covers the sheet and its surround.
- A press that resolved no text position (before the first arrange, or after a large scrollbar jump) raised the drag flag and returned without capturing the pointer, so no release could clear it: every later button-free move over the view extended the selection and auto-scrolled with no button held.
- A block container opened inside a list item escaped to the document root as an empty sibling, taking its content with it. Visible in markdown as a stray blockquote bar above a list (`- > quote in list item`), and the same for a heading, an HTML block or a table inside a list item.
- Inserting a block into a document whose blocks are not all realized (one loaded from a file) used the child index as the logical-children index, which threw or misordered the logical tree once realized siblings were missing from that list; the element now goes before the first realized following sibling.
- A selection dragged past the end of the body no longer runs into the footnotes: the body is one document and the notes are others, so the selection stops at the body's end and Ctrl+A selects the body alone.
- Snapshot capture takes a hyperlink's `NavigateUri.OriginalString` instead of `ToString()`: URI canonicalization appended a trailing slash to authority-only URLs, which rewrote every realized link on save.
- The round