CodeEdit.Maui 0.3.0-preview.1

This is a prerelease version of CodeEdit.Maui.
dotnet add package CodeEdit.Maui --version 0.3.0-preview.1
                    
NuGet\Install-Package CodeEdit.Maui -Version 0.3.0-preview.1
                    
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="CodeEdit.Maui" Version="0.3.0-preview.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CodeEdit.Maui" Version="0.3.0-preview.1" />
                    
Directory.Packages.props
<PackageReference Include="CodeEdit.Maui" />
                    
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 CodeEdit.Maui --version 0.3.0-preview.1
                    
#r "nuget: CodeEdit.Maui, 0.3.0-preview.1"
                    
#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 CodeEdit.Maui@0.3.0-preview.1
                    
#: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=CodeEdit.Maui&version=0.3.0-preview.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=CodeEdit.Maui&version=0.3.0-preview.1&prerelease
                    
Install as a Cake Tool

CodeEdit.Maui

A native code editor for .NET MAUI, built in C# on RichEdit.Maui. It uses the same native text surfaces on Windows, Android, iOS, and Mac Catalyst.

The control provides C# syntax coloring, logical line numbers, horizontal scrolling or word wrapping, and light/dark palettes. It composes a RichEditor through public document, selection, clipboard, and history APIs. The sample app supplies toolbar actions and literal find/replace.

Syntax colors are view-owned decorations. Highlighting and theme changes do not change document content, versions, saved state, content events, or undo history. Persist source through Document.Text; clipboard copies contain source without syntax formatting.

Register and create an editor

Reference CodeEdit.Maui and register its handler during MAUI startup. UseCodeEditor() also registers RichEdit.Maui:

using CodeEdit.Maui;
using RichEdit.Maui;

builder.UseMauiApp<App>().UseCodeEditor();

var editor = new CodeEditor
{
    Document = CodeDocument.FromPlainText("public class Example\n{\n}\n"),
    Theme = CodeEditorTheme.Dark,
    FontSize = 14,
    IndentSize = 4,
    ShowLineNumbers = true,
    WordWrap = false,
};

The package uses the matching RichEdit.Maui version, including its general decoration, directional-selection, and document-history APIs.

XAML and MVVM work with the same control:

<code:CodeEditor
    xmlns:code="clr-namespace:CodeEdit.Maui;assembly=CodeEdit.Maui"
    x:Name="Editor"
    Document="{Binding Document}"
    SelectedRange="{Binding Selection, Mode=TwoWay}"
    IsReadOnly="{Binding IsReadOnly}"
    ShowLineNumbers="True"
    WordWrap="False" />

Give the editor a bounded area, such as a Grid row with Height="*". It owns native scrolling. The test app's Code editor tab demonstrates the control with editing, search, wrapping, and theme controls; its page is written entirely in C#.

Work with source

Load plain source with CodeDocument.FromPlainText. Line endings are normalized to \n, and a newly created document starts with empty undo history. Persist source using Document.Text:

var source = editor.Document.Text;

editor.Document.Edit(edit =>
    edit.InsertText(editor.Document.Length, "// Added by the application\n"));

editor.SelectedRange = new RichTextRange(0, 6);
editor.Selection.ReplaceText("internal");

editor.Undo();
editor.Redo();

Document is a CodeDocument, and Selection is a CodeTextSelection. Their public APIs expose plain source operations. A document can belong to one editor at a time. Pasting allows cancellation or replacement before the accepted fragment is converted to plain text.

A CodeDocument has immutable source snapshots, atomic text edits, public undo/redo, undo descriptions, and IsModified. CreateSavePoint() captures the state associated with a snapshot; after an asynchronous save succeeds, MarkSaved(savePoint) marks that captured state without marking later edits as saved. Undoing back to the saved state clears IsModified.

Editing and navigation

Commands exposes the core ICommand instances: Undo, Redo, SelectAll, Copy, Cut, and Paste. The corresponding methods are also available directly. Toolbar actions such as indentation, newline insertion, line comments, find/replace, and go-to-line belong to the application. The sample implements them in CodeEditorActions.cs using public document, selection, line, and scrolling APIs.

Setting Default Behavior
IndentSize 4 Width for space indentation; valid values are 1–16
UseTabs false Insert a tab instead of spaces; display tab stops follow the native editor
AutoIndent true Copy leading whitespace when inserting a newline
LineCommentPrefix // Prefix used by the line-comment toggle
WordWrap false Wrap long lines when enabled; otherwise scroll horizontally
ShowLineNumbers true Show logical line numbers aligned with native layout
MaxLength -1 Maximum UTF-16 source length, or unlimited

Default hardware bindings use Tab and Shift+Tab to indent and outdent, and Control+/ (Command+/ on Apple platforms) to toggle line comments. The input settings above configure keyboard behavior and the sample toolbar. Enter inserts a newline with optional indentation; automatic indentation after native newline input is merged with that input's undo unit.

The sample applies block actions and replace-all through one Document.Edit transaction, which remaps the selection and records one undo unit. Its actions check read-only state and the length limit before editing. Its line-selection policy excludes a final line when the selection ends at that line's start.

LineCount includes a trailing empty line. CaretPosition reports the active endpoint as a one-based logical line and UTF-16 column. SelectionState holds the anchor and active offsets and binds two ways. SelectedRange is a normalized range; assigning it selects forwards. Undo/redo restore the recorded selection. GetPosition and GetOffset convert coordinates, and ScrollIntoView reveals a range without selecting it. Tabs occupy one UTF-16 column; visual wrapping does not add logical lines.

RichTextRange line = editor.GetLineRange(12); // Excludes the line terminator.
int offset = editor.GetOffset(new CodePosition(12, 5));
editor.Selection.Select(new RichTextSelectionState(offset, offset));
editor.ScrollIntoView(editor.SelectedRange);

The sample's CodeEditorActions supplies GoToLine, FindAll, FindNext, FindPrevious, ReplaceAll, and CodeSearchOptions. Its searches are ordinal and literal, with application-defined word boundaries and optional wraparound.

Key bindings and context menus

KeyBindings, KeyDown, and ContextMenuOpening operate on the inner text surface. The bindings collection includes CodeEditor's Tab, Shift+Tab, Enter, line-comment, and rich-format-shortcut suppression defaults. Applications can replace or remove any binding; the last matching entry takes precedence.

var primary = OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst()
    ? EditorKeyModifiers.Meta
    : EditorKeyModifiers.Control;
editor.KeyBindings.Add(new(EditorKey.F, primary, findCommand));

editor.ContextMenuOpening += (_, args) =>
{
    args.Items.Add(new MenuFlyoutSeparator());
    args.Items.Add(new MenuFlyoutItem { Text = "Find…", Command = findCommand });
};

KeyDown.Handled suppresses further key handling. Bindings check CanExecute, pass their command parameter, and preserve native IME composition. These APIs handle hardware keyboards; soft-keyboard edits continue through native text input.

Set IncludeDefaultItems = false in ContextMenuOpening to supply a replacement menu, or assign a MenuFlyout through FlyoutBase.SetContextFlyout(editor, menu). The attached menu also supplies the inner text surface's editing menu. Menu commands can call application-owned helpers such as the sample's search and indentation actions.

The shared input and menu API documents modifier semantics and supported menu fields. Native menu customization is available on Windows, Android, and iOS/Mac Catalyst 16 or later; earlier Apple versions use their standard menus.

Syntax and themes

CSharpSyntaxHighlighter is the default. It classifies keywords, strings and character literals, comments, numbers, and preprocessor directives, including verbatim and raw strings. It performs lexical coloring: contextual keywords are always colored, and interpolation expressions are not parsed separately. Compiler diagnostics, completion, semantic classification, and folding are outside this version's scope.

Implement ICodeSyntaxHighlighter to supply another language or compiler-based classification. Return ordered, nonempty, non-overlapping CodeToken ranges using UTF-16 offsets. Unclassified text uses the theme's TextColor.

Highlighting is debounced by 120 ms and runs on a worker thread over an immutable source string. Highlighters must be thread-safe and should observe cancellation. Results are validated and discarded when superseded, when the document changes, or when the handler disconnects. Presentation waits until IME composition has ended and replaces the syntax decoration layer. CodeEditor reclassifies text after undo/redo. Application layers created through Decorations.CreateLayer() compose above syntax colors, so diagnostics or search highlights can be managed independently.

editor.Highlighter = new CSharpSyntaxHighlighter();
await editor.RefreshHighlightingAsync(); // Call on the UI thread; also useful in tests.

editor.Theme = CodeEditorTheme.Dark with
{
    CommentColor = Color.FromArgb("#85B98C"),
};

editor.Highlighter = null; // Disable syntax coloring.

Automatic failures raise HighlightingFailed; an awaited explicit refresh propagates errors to its caller. Highlighting scans the complete source and updates changed presentation ranges. Native readback removes presentation overrides before committing authored content. This version is intended for ordinary source documents, not a virtualized multi-gigabyte file viewer.

Validation

The library targets .NET 10 on Windows, Android, iOS, and Mac Catalyst. Windows integration tests use a real WinUI RichEditBox and cover native input, focus changes, undo/redo, clipboard conversion, scrolling, asynchronous cancellation, and the sample editing helpers. Native adapters in the code package use public platform APIs for keyboard shortcuts, IME state, wrapping, and visible-line geometry; they do not read or change RichEditor internals.

Build and run the focused tests on Windows with the installed MAUI workload and Windows App SDK runtime:

dotnet build RichEdit.Maui.Tests/RichEdit.Maui.Tests.csproj -p:Platform=x64 -p:WindowsPackageType=None
& ./RichEdit.Maui.Tests/bin/x64/Debug/net10.0-windows10.0.19041.0/RichEdit.Maui.Tests.exe -class RichEdit.Maui.Tests.CodeEditorTests

The executable uses xUnit's in-process runner. Add -p:SkipMauiWorkloadManifest=true to the build when validating the .NET 10 targets with the locally installed workload. Mobile native behavior requires device or simulator validation.

Threading and notifications

Create and mutate controls on their owning UI thread. An attached source document requires that thread for edits, history operations, and save-state changes; violations throw before committing. Detached documents require callers to serialize mutations. Capture an immutable CurrentSnapshot on the owning thread for background inspection.

Internal document/native/selection synchronization completes before public notifications. The order is document Changed, control ContentChanged/TextChanged, document property notifications, then selection and selection-format notifications. Mutating the same document recursively from these notifications throws.

Clipboard operation exceptions propagate from awaited CopyAsync, CutAsync, and PasteAsync. Clipboard ICommand and native-menu entry points propagate asynchronous failures to the application's UI exception handling; the library does not log and suppress them.

Product Compatible and additional computed target framework versions.
.NET net10.0-android36.0 is compatible.  net10.0-ios26.0 is compatible.  net10.0-maccatalyst26.0 is compatible.  net10.0-windows10.0.19041 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0-preview.1 29 9/7/2026