[XEXPR] [Experimental] XAML C# Expressions#33693
Conversation
64db388 to
507dc00
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces XAML C# Expressions, an experimental SourceGen-only feature that enables embedding C# expressions directly in XAML property values. The feature provides automatic detection of whether identifiers reference this (page/view) or x:DataType (BindingContext) members, generating either compile-time SetValue calls or runtime TypedBinding instances with INPC handlers.
Changes:
- New SourceGen infrastructure for detecting and processing C# expressions in XAML markup
- Automatic member resolution distinguishes between local (
this) and binding (x:DataType) properties - Support for lambda event handlers (including async) and string interpolation
- Comprehensive test coverage (63 tests total) with diagnostic error scenarios
- Full specification and documentation for the experimental feature
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
CSharpExpressions.sgen.xaml.cs |
Comprehensive test suite with 46 integration tests covering expressions, bindings, events, and diagnostics |
CSharpExpressions.sgen.xaml |
XAML test file demonstrating all expression syntax variants |
MemberResolverTests.cs |
14 unit tests for member resolution logic (this vs x:DataType) |
CSharpExpressionDiagnosticsTests.cs |
4 diagnostic tests for ambiguous members and not-found errors |
XDataTypeResolver.cs |
Helper to extract x:DataType from XAML element tree (extracted from existing code) |
ExpandMarkupsVisitor.cs |
Expression detection in markup visitor with disambiguation logic |
SetPropertyHelpers.cs |
TypedBinding generation for expressions and lambda event handler support |
NodeSGExtensions.cs |
Extension methods to handle Expression values during code generation |
MemberResolver.cs |
Core logic for resolving identifiers to this/x:DataType with explicit prefix support |
ExpressionAnalyzer.cs |
Roslyn-based expression analysis extracting INPC handlers and local captures |
Descriptors.cs |
Three new diagnostic descriptors (MAUIX2007, MAUIX2008, MAUIX2009) |
CSharpExpressionHelpers.cs |
Detection and transformation utilities (quote conversion, lambda detection) |
AnalyzerReleases.Unshipped.md |
Analyzer release notes for new diagnostics |
XamlCSharpExpressions.md |
Complete specification with syntax reference and examples |
xaml-csharp-expressions.instructions.md |
GitHub Copilot instructions for using the feature |
src/Controls/tests/SourceGen.UnitTests/CSharpExpressionDiagnosticsTests.cs
Outdated
Show resolved
Hide resolved
|
R.I.P InvertedBoolConverter 2014-2026 💀 |
|
Awesome! |
|
This is a game changer! |
|
Even in my wildest dreams, that didn't exist Awesome work |
|
thank you, please approve like yesterday!!! |
|
This is awemazing, improving Xaml DevX is the biggest thing anyone can do for MAUI. I've dreamt of days like this. |
|
Can we have this in WPF, UWP and WinUI3 too? It is amazing. |
|
Having |
2719862 to
a296814
Compare
|
This is a game-changer! This is exactly the direction .NET MAUI needs to take |
|
Consider making it feature-complete by adding support for the OR operator too.
|
<Lable IsVisible={async () => {
bool canVisible = false;
white(true)
{
if(canVisible)
{
canVisible = false;
return false;
}
awite Task.Delay(TimeSpan.FromSecond(5));
canVisible = true;
return true;
}
}
}}c# inside xmal. |
We've consider using We're not creating a new language here, and keeping backward compatibility is our first priority. This is why |
|
@egvijayanand This already works! Any valid C# expression is supported, including <Label IsVisible="{A || B}" />
<Label IsVisible="{IsLoaded && ShowContent}" /> |
Great to hear about it 👍. |
|
@jonathanpeppers razor doesn't have to be valid xml, we like that XAML stays processable by xml tools. For more complex expression that would otherwise require a lot of escaping (quotes, <, >, &), I'm adding support for CDATA |
@StephaneDelcroix thank you for your response, your work and the addition of CDATA. With the addition of CDATA, it seems a bit too much. could everything that starts with For instance
This may not be valid XML If everything within only |
f465454 to
d72a9a9
Compare
simonrozsival
left a comment
There was a problem hiding this comment.
I proposed several changes (esp. around auto escaping embedded C# code) but I believe we have room for breaking changes since this is an experimental feature. Let's move ahead and revisite it based on feedback from customers using it in real-world apps.
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
| return; | ||
|
|
||
| // Check if it's a C# expression | ||
| if (CSharpExpressionHelpers.IsExpression(trimmed, name => TryResolveMarkupExtensionType(name, node.NamespaceResolver))) |
There was a problem hiding this comment.
Is there any chance that existing, valid XAML, CSharpExpressionHelpers.IsExpression could return true?
If not, this seems reasonable to require $(EnablePreviewFeatures) to opt-in. $(EnablePreviewFeatures) is also viral, so if an assembly declares it, you have to declare it to use that assembly (or NuGet).
There was a problem hiding this comment.
in doubt, if both a markup and an expression could be resolved, it'll error
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
Experimental feature for .NET 11 - write C# expressions directly in XAML.
Features:
- Property bindings: {.PropertyName}, {this.PropertyName}
- String interpolation: {$'{First} {Last}'}
- Lambda event handlers: {(s, e) => DoSomething()}
- Compound expressions with operators
- AND/OR word aliases for XML compatibility
- Semantic char/string detection for single-quoted literals
- Custom markup extension semantic lookup
Diagnostics:
- MAUIX2007: Markup extension ambiguity warning
- MAUIX2008: Page/BindingContext member conflict error
- MAUIX2009: Member not found error
- MAUIX2010: Non-settable expression info
- MAUIX2012: Parameter list expression error
- MAUIX2013: Async lambda error
- Fix handling of escaped backslash followed by quote (e.g., '\"') - Count consecutive backslashes to determine if quote is escaped - Update C# version comment from 14 to 13
- Always transform single quotes to double quotes in GetExpressionCode - TransformQuotesWithSemantics now converts single-char strings back to char literals where method parameters expect char type - Removes unused TransformQuotes property from Expression record - Fixes CS1012 (char literal) and CS1056 (unexpected $) build errors
C# Expressions only work with SourceGen mode, not Runtime or XamlC. Changed test from [Theory] with [XamlInflatorData] to [Fact] that explicitly uses XamlInflator.SourceGen.
4683c74 to
b9bd7a4
Compare
|
/azp run maui-pr-devicetests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
XAML C# Expressions were introduced in #33693 as an experimental feature gated behind EnablePreviewFeatures. For .NET 11, this feature graduates to GA, so the gating is no longer needed. Changes: - Remove MAUIX2012 diagnostic (CSharpExpressionsRequirePreviewFeatures) - Remove EnablePreviewFeatures checks in ExpandMarkupsVisitor - Remove EnablePreviewFeatures property from ProjectItem - Remove CompilerVisibleProperty for EnablePreviewFeatures from targets - Update test infrastructure to remove EnablePreviewFeatures parameter - Remove EnablePreviewFeatures from test project files
|
They should sustitute & for and |
|
This expression works fine for regular properties, but does not detect command properties. Is there any specific reason? Error message: Property or indexer 'MainViewModel.IncrementCommand' cannot be assigned to -- it is read only. It seems that it tries to assign a value to the read-only Command property. In general, command properties are intended to be read-only, featuring only a getter. Suggest creating a discussion so that feedback can be viewed in one place. |


Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
XAML C# Expressions
Write C# expressions directly in XAML — no more converters, no more boilerplate.
This is an experimental preview feature for .NET 11. The API and syntax may change before release.
How to Enable
Add to your project file:
Without this flag, using C# expression syntax results in error MAUIX2012.
Requirements
x:DataTypeattribute on your page/viewExample
Syntax Highlights
Some of the supported expressions (see full spec for complete reference):
{Property}{Username}{A.B}{User.DisplayName}{$'..{x}..'}{$'Hello {Name}!'}{!Bool}{!IsHidden}{A && B}{IsLoaded && HasData}{A * B}{Price * Quantity}{c ? a : b}{IsVip ? 'Gold' : 'Standard'}{a ?? b}{Title ?? 'Untitled'}{(s, e) => ...}{(s, e) => Count++}{Method()}{GetDisplayText()}{Type.Member}{DateTime.Now}What's NOT Supported
{async (s, e) => ...}) — use regular methods{() => ...}) — must include(s, e)Resources