Migrate to 0.5.0-preview.1
This is a breaking release. Upgrade the MAUI packages together and restore their TextEdit.Core and RichEdit.Core dependencies. The libraries target .NET 10.
The rich control now takes a structured RichEdit.Core.RichDocument. Source editing uses CodeDocument over the separate text core. Both controls share native infrastructure; CodeEditor does not wrap the public RichEditor. A document can serve multiple views on one dispatcher.
API replacements
| Existing application code | 0.5 API |
|---|---|
RichTextDocument.FromPlainText(text) assigned to RichEditor |
RichDocument.FromPlainText(text) |
RichTextDocument.FromRtf(rtf), Document.RtfText |
RtfCodec.ReadAsync / WriteAsync over streams |
Flat rich RichTextRange for every operation |
RichTextPoint and typed RichSelection; use snapshot ProjectText only as an explicit mapping |
Mutable editor.Selection formatting facade |
editor.ApplyCharacterFormat, toggle methods, editor.Session, and editor.Edit |
RichTextSelectionState |
Source TextSelectionState or TextSelectionSet; rich RichTextSelection or RichTextSelectionSet |
Source RichTextRange |
TextEdit.Core.TextSpan |
Source RichTextDocumentRevision |
TextEdit.Core.DocumentRevision |
| Rich formatting records with MAUI colors/point doubles | RichCharacterStyle, RichParagraphStyle, RGB RichColor, twip RichLength, half-point RichFontSize |
| Null/default as a general formatting reset | Explicit StyleValue<T>.Inherit, Initial, or value, as appropriate |
CreateSavePoint plus a separately captured snapshot |
One CaptureSave() result, acknowledged by MarkSaved(capture) |
MergeWithPrevious |
Session-controlled typing coalescing or an explicit BeginUndoGroup |
Authored PreserveHistory formatting |
Ordinary authored transaction, or a transient decoration layer |
Rich Decorations / Adornments with flat offsets |
editor.Presentation producers with typed rich ranges/anchors |
Source Decorations / Adornments |
Retained source presentation concepts, now using text-core coordinates and generation-checked requests |
| Generic persisted metadata | Supported document semantics such as comments/bookmarks; application metadata remains application-owned |
Use RichDocument as the document shared by rich-editor controls.
Load existing RTF with diagnostics
using RichEdit.Core;
using RichEdit.Core.Rtf;
var result = await RtfCodec.ReadAsync(inputStream);
if (!result.Diagnostics.IsEmpty)
{
// Apply the application's import policy before replacing the open document.
}
editor.Document = new RichDocument(result.Snapshot);
There is no format-conversion step to HTML, JSON, or a package file. RTF remains the rich format. Set RequireLossless = true when any diagnosed approximation must reject import.
Review RTF support against your documents. The structured authored profile includes semantic tables and revisions but does not represent every flat formatting/list option: arbitrary numeric font weights, custom list prefix/suffix and marker fonts/picture bullets, custom metadata, and paragraph min/max-height pairs need an application policy. Do not silently translate an unsupported feature and then mark the file as losslessly saved. The core uses bold/normal, standard list counters, and explicit line-spacing modes.
Standard Word tracked-change controls are not converted into the retained revision model. The codec's own revision metadata round-trips supported pending groups, while independent RTF readers receive the current body.
Convert a flat rich selection
Obtain the mapping from the exact rich snapshot being edited:
using RichEdit.Core;
var snapshot = editor.Document.CurrentSnapshot;
var projection = snapshot.ProjectText();
var start = projection.GetPoint(startOffset);
var end = projection.GetPoint(endOffset);
if (start is not null && end is not null)
{
editor.Selection = new RichEdit.Core.RichTextSelection(start.Value, end.Value);
editor.ApplyCharacterFormat(new RichCharacterStyle { Bold = true });
}
An offset can address a structural separator with no text point. A cell selection or block command must handle that intent explicitly. Do not force the separator into a neighboring text range. Store long-lived locations as rich IDs/anchors or semantic annotations, not offsets in an obsolete projection.
Move formatting to transactions
editor.Edit(edit =>
{
edit.SetCharacterFormat(point, length,
new RichCharacterStyle { Bold = true, Italic = false });
}, new RichEditOptions(Description: "Apply emphasis"));
Set-format APIs replace the direct record for the requested range. Preserve other direct properties when needed. Selection-format commands handle selected content or typing state. Compute mixed toolbar values from resolved selected formats instead of binding to the removed mutable selection-format facade.
Table editing uses RichCellSelection, GetTableGrid, and explicit row/column/merge/split commands. Lists use RichList/RichListItem and wrap/unwrap/split/join commands.
Update source extensions
Import TextEdit.Core for spans, revisions, tracking, and selection sets. Source line endings now remain exact: remove application code that assumes every newline occupies one UTF-16 unit. GetLine provides the terminator length. Normalizing EOLs is an explicit undoable edit.
Use TryApplyEdits with the captured revision and optional selection generation for completion/refactoring. Move dependent typing reactions into InputRules; a public change callback cannot recursively edit the live document. Batch edit coordinates refer to one captured snapshot.
For async colors/widgets, call BeginUpdate before background work and TryPublish on return. A revision-only check cannot reject an older result computed with different settings against unchanged text.
Shared views, composition, and saving
Create separate controls with the same document. Each control owns its session, selection, presentation, and viewport. Undo is global to the document and navigates the invoking view; applications expecting pane-local undo must adjust their UI.
For linked source panes, use CaptureScrollAnchor and RestoreScrollAnchorAsync instead of copying native pixel offsets. Anchors follow edits and dispose independently of the control. Use RevealAsync for explicit reveal alignment, and forward ViewportChanged.CauseToken to suppress linked-scroll feedback. Keep ScrollIntoView for simple range visibility. Rectangle gestures use visual glyph intervals, so a bidi row can contribute more than one source selection.
During IME, the document snapshot is committed content and the session composition is provisional. Resolve native composition before saving or switching documents. Respect CompositionActive rejection instead of bypassing the session.
if (!editor.TryCompleteComposition()) return;
var document = editor.Document;
var capture = document.CaptureSave();
await RtfCodec.WriteAsync(capture.Snapshot, outputStream);
// Flush and atomically replace the file before acknowledging.
document.MarkSaved(capture);
Validation checklist
- Compile all document, selection, formatting, and extension call sites.
- Import representative RTF with diagnostics and compare semantic re-export/reload.
- Exercise multiple views, undo, save during editing, and document replacement.
- Exercise native composition, clipboard, table navigation, and accessibility on target devices.
- Check responsiveness with your application's file sizes, hints, and longest lines.