-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Summary
Add support for the x:Code XAML directive, allowing developers to define C# members (methods, fields, properties) directly inside XAML files without a separate code-behind file.
This becomes a natural complement to XAML C# Expressions (XEXPR). Where XEXPR enables inline expressions in attribute values ({= expr}, lambdas, etc.), x:Code enables inline member declarations — event handlers, helper methods, backing fields — completing the "single-file component" story.
Motivation
With XEXPR, developers can already write:
<Button Text="Add" Clicked="{(s, e) => Items.Add(new Item())}" />But anything beyond a one-liner forces a code-behind file. x:Code closes the gap:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MainPage">
<x:Code><![CDATA[
ObservableCollection<Item> Items { get; } = new();
void OnRefresh(object sender, EventArgs e)
{
Items.Clear();
foreach (var item in DataService.Load())
Items.Add(item);
}
]]></x:Code>
<VerticalStackLayout>
<Button Text="Refresh" Clicked="{OnRefresh}" />
<CollectionView ItemsSource="{Items}" />
</VerticalStackLayout>
</ContentPage>Scenarios unlocked
| Scenario | Today | With x:Code |
|---|---|---|
| Simple event handler | Requires .xaml.cs file |
Inline in XAML |
| Bindable helper property | Requires .xaml.cs file |
Inline in XAML |
| Self-contained sample/snippet | Two files minimum | Single .xaml file |
| DataTemplates with per-template logic | External static helpers | Colocated code |
Hot Reload
Hot Reload is supported through the existing C# Hot Reload infrastructure. Since x:Code blocks are emitted as regular members of the partial class by the source generator, edits to x:Code content are standard C# member changes from the compiler's perspective and flow through the C# HR pipeline.
Constraints (same as WPF + MAUI-specific)
WPF-inherited constraints
These match the WPF x:Code specification:
x:Codemust be an immediate child element of the root element.x:Classmust be provided on the root element.- Code is emitted into the scope of the existing partial class — all declarations become members/variables of that class.
- Cannot define additional top-level classes (nesting is allowed but atypical).
- Cannot define or add CLR namespaces beyond the partial class namespace — external types must be fully qualified (or use
usingaliases inside the block if the language supports it). - Conflicts with code-behind members are compiler errors — standard partial class rules apply.
MAUI-specific constraints
- Requires XAML Source Generator (XSG) —
x:Codeis not supported by runtime XAML inflation or XamlC IL rewriting. Only the source generator pipeline can emit the code block into the generated partial class. - Requires
<EnablePreviewFeatures>true</EnablePreviewFeatures>— same gating as XEXPR (diagnosticMAUIX2012when missing). - Content should be wrapped in
<![CDATA[...]]>— recommended to avoid XML-escaping<,>,&in C# code. The parser already handles CDATA nodes. - Multiple
x:Codeblocks are allowed — each is concatenated into the partial class body (order-preserved). This keeps code colocated with the UI section it supports.
Interaction with XEXPR
x:Code and XEXPR are complementary:
- XEXPR → inline expressions in attribute values (bindings, event lambdas, computed values)
- x:Code → member declarations (methods, properties, fields)
Members declared in x:Code are accessible from XEXPR expressions via this. (or bare identifier if unambiguous), following existing XEXPR resolution rules (MAUIX2007/2008 diagnostics for ambiguity).
Diagnostics
| Code | Condition |
|---|---|
MAUIX2012 |
EnablePreviewFeatures not set (reuse existing) |
| New | x:Code not an immediate child of root element |
| New | x:Code used without x:Class on root |
| New | x:Code used with runtime or XamlC inflator (XSG required) |
Open questions
1. x:Code in XAML without x:Class (ResourceDictionary)
Today the source generator already compiles XAML files without an explicit x:Class (primarily ResourceDictionary files) by synthesizing a temporary class. Should x:Code be supported in that scenario?
Arguments for: A ResourceDictionary with value converters or helper methods defined inline via x:Code would be fully self-contained — no satellite .cs file needed.
Arguments against: The synthesized class is an implementation detail with no stable identity; members declared in x:Code would exist on a type that consumers can't reference. This may create confusion about member visibility and testability.
Tentative answer: Defer — require x:Class initially (constraint #2). Revisit if there's demand for self-contained RD files.
2. Follow-up: make .xaml.cs code-behind optional
With x:Code, the code-behind file becomes redundant for simple pages. As a follow-up, the source generator should emit a complete code-behind (including constructor and InitializeComponent() call) when no .xaml.cs file is present — making true single-file XAML components possible:
<!-- No .xaml.cs file needed -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.SimplePage">
<x:Code><![CDATA[
void OnTap(object sender, EventArgs e) => Title = "Tapped!";
]]></x:Code>
<Button Text="Tap me" Clicked="{OnTap}" />
</ContentPage>This is a separate work item that depends on x:Code landing first.
3. IDE integration (code completion, diagnostics)
x:Code blocks contain C# code embedded inside XAML (typically inside <![CDATA[...]]>). The source generator will emit this code verbatim into a partial class, so compiler diagnostics will surface — but with mapped locations pointing into the XAML file, which IDEs may or may not handle gracefully.
Key questions:
- Code completion: Can VS / VS Code provide IntelliSense inside
x:CodeCDATA blocks? This likely requires IDE-side awareness of the embedded language context (similar to how Razor handles@code {}blocks). Without it, developers get correctness (compiler errors) but lose discoverability. - Syntax highlighting: XAML editors currently treat CDATA as opaque text. C# highlighting inside
x:Codewould require editor extensions or language server cooperation. - Error mapping: The source generator can emit
#linedirectives to map errors back to XAML line numbers. This works for compiler errors but may not integrate with IDE inline error display inside CDATA.
Tentative answer: Ship x:Code without IDE-specific tooling initially — compiler errors and HR work out of the box. IDE richness (completion, highlighting) is a tooling follow-up that can be pursued independently, potentially by the VS XAML Language Service team.
Non-goals
x:Codein non-root elements (e.g., inside DataTemplates) — out of scope for initial implementation.- Multi-language support — C# only, matching XEXPR.
- Runtime interpretation — compilation only, no eval/scripting.