Skip to content

XAML xmlns simplifications #29579

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: net10.0
Choose a base branch
from
Open

Conversation

StephaneDelcroix
Copy link
Contributor

@StephaneDelcroix StephaneDelcroix commented May 19, 2025

Description of Change

This PR includes multiple changes related to xmlns, as tracked by #28836.

New global xmlns

http://schemas.microsoft.com/dotnet/maui/global is a new xmlns that you can use to aggregate multiple xmlns together.

The scope of it is local to the project.

By default, it contains the maui xmlns (sourcegenerated) YourNamespace and YourNamespace.Pages (enabled by the updated template.

You can extend this in e.g. GlobalXmlns.cs added by the template (#29203), or adding assembly level XmlnsDefinition anywhere.

You can ad CLR namespaces as usual, but also other xmlns

[assembly:XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "http://mycompany.com/schema/myxmlns")]

That global xmlns also have a XmlnsPrefix "global" associated to it.

IMPORTANT NOTE: you can not aggregate the x xmlns into the global one. It plays a special role for the parsers and inflators.

This change is implemented using a source generator, so there's no requirement to change any other tool consuming xaml making use of this

Make the xmlns delcarations in XAML implicit

The XAML inflators uses the global xmlns as default, and preloads all other xmlns declared by a XmlnsDefinition that also have a XmlnsPrefix attribute. e.g. the x: prefix is there to be used without the need of being declared.

<ContentPage x:Class="MyNamespace.MyPage" Title="no default xmlns">
    <VerticalStackLayout>
        <Label x:Name="label" Text="No xmlns:x declaration, but x: usage anyway" />
    </VerticalStackLayout>
</ContentPage>

This part is opt-in, you can enable it by setting

  <PropertyGroup>
    <DefineConstants>$(DefineConstants);MauiAllowImplicitXmlnsDeclaration</DefineConstants>
    <EnablePreviewFeatures>true</EnablePreviewFeatures>
  </PropertyGroup>

in your .csproj

Protect some xmlns

with a fix for #28150, it becomes necessary to avoid the overload of the maui, and x, xmlns namespaces.

Issues Fixed

@Copilot Copilot AI review requested due to automatic review settings May 19, 2025 19:24
@StephaneDelcroix StephaneDelcroix requested a review from a team as a code owner May 19, 2025 19:24
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds a new global XAML xmlns, enables implicit xmlns declarations via a preview feature flag, protects built-in xmlns entries, and updates related source generators and metadata.

  • Introduces AllowImplicitXmlnsDeclarationAttribute and wiring in build tasks to support implicit/default xmlns usage.
  • Changes XmlnsDefinitionAttribute constructor parameter name and surfaces a new Target property.
  • Updates public API shipped/unshipped files, documentation, and solution to expose new types and behaviors.

Reviewed Changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
PublicAPI/netstandard/PublicAPI.Shipped.txt Updated XmlnsDefinitionAttribute ctor signature
PublicAPI/net/*.Unshipped.txt & *.Shipped.txt (all platforms) Added AllowImplicitXmlnsDeclarationAttribute and Target API
Properties/AssemblyInfo.cs Re-added x: xmlns prefix for XAML
AllowImplicitXmlnsDeclarationAttribute.cs New attribute to opt into implicit xmlns support
Build.Tasks/XmlTypeExtensions.cs Extended XML namespace gathering and added prefix extraction
Build.Tasks/XamlTask.cs Wired implicit xmlns logic into XAML parsing
Build.Tasks/XamlCTask.cs Updated XAML compilation to pass module context
docs/XmlnsDefinitionAttribute.xml Updated ctor sig in docs and added Target member
Microsoft.Maui-vscode.sln Included new SourceGen unit test project
Comments suppressed due to low confidence (1)

src/Controls/src/Build.Tasks/XmlTypeExtensions.cs:45

  • The new logic protecting built-in xmlns definitions and throwing BuildException on invalid entries lacks corresponding unit tests. Please add tests covering both allowed and prohibited XmlnsDefinitionAttribute scenarios to validate this behavior.
if (   attribute.XmlNamespace != XamlParser.MauiGlobal

@StephaneDelcroix StephaneDelcroix changed the base branch from main to net10.0 May 19, 2025 19:39
@StephaneDelcroix StephaneDelcroix changed the base branch from net10.0 to main May 19, 2025 19:42
@StephaneDelcroix StephaneDelcroix added this to the .NET 10 Planning milestone May 20, 2025
@jfversluis jfversluis added the area-xaml XAML, CSS, Triggers, Behaviors label May 20, 2025
@StephaneDelcroix StephaneDelcroix force-pushed the dev/stdelc/xmlns2025 branch 3 times, most recently from dd36fc7 to 5172f61 Compare May 22, 2025 12:06
@jfversluis jfversluis moved this from Todo to Ready To Review in MAUI SDK Ongoing May 23, 2025
Make the default xmlns implicitely pointing to maui default xmlns
pre-load all available XmlsPrefixes for use

the capability is disabled by default, and you need to opt-in

- fixes #28849
- fixes #28850
- fixes #28847
Allow aggregating multiple xmlns into a single new global
http://schemas.microsoft.com/dotnet/maui/global using XmlnsDefinition
attribute, like this

```csharp
[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "http://schemas.microsoft.com/dotnet/2021/maui")]
```

the x: xmlns can not be aggregated, as it serves important purposes for
the parsers

this also brings #28090 to fix #28150

- fixes #28150
- fixes #28843
- closes #28090
protect maui and x: xmlns from overloading.

- fixes #28836
@StephaneDelcroix StephaneDelcroix changed the base branch from main to net10.0 May 26, 2025 11:37
@StephaneDelcroix StephaneDelcroix requested a review from Copilot May 26, 2025 19:55
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR simplifies XAML xmlns handling by introducing a project-scoped global xmlns, enabling implicit xmlns declarations, and protecting core namespaces through validation. It also updates public APIs and build tasks to support these features.

  • Added AllowImplicitXmlnsDeclarationAttribute and updated XAML parsing to honor implicit xmlns.
  • Introduced validation to protect maui and x namespaces and aggregated global xmlns logic in source generator.
  • Changed the public API signature of XmlnsDefinitionAttribute and updated its documentation.

Reviewed Changes

Copilot reviewed 59 out of 59 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Controls/src/Core/PublicAPI/net-*/PublicAPI.Shipped.txt Updated XmlnsDefinitionAttribute constructor signature to (string xmlNamespace, string target)
src/Controls/src/Core/Properties/AssemblyInfo.cs Added implicit x prefix for the XAML namespace
src/Controls/src/Core/AllowImplicitXmlnsDeclarationAttribute.cs New attribute to opt into implicit xmlns declarations
src/Controls/src/Build.Tasks/XmlTypeExtensions.cs Added protected xmlns validation and global xmlns gathering logic
src/Controls/src/Build.Tasks/XamlTask.cs Updated ParseXaml to use implicit xmlns via AllowImplicitXmlnsDeclarationAttribute
src/Controls/src/Build.Tasks/XamlCTask.cs Adjusted ParseXaml call to pass ModuleDefinition
src/Controls/src/Build.Tasks/TypeReferenceExtensions.cs Refactored system‐assembly comparison and fixed comparer logic
src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs Extended DataTemplate handling to include global xmlns and used helper
src/Controls/src/Build.Tasks/ModuleDefinitionExtensions.cs Enhanced type resolution to support global xmlns URIs
src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs Added IsOfAnyType helper to unify Maui vs. global xmlns checks
src/Controls/docs/Microsoft.Maui.Controls/XmlnsDefinitionAttribute.xml Updated docs for constructor parameter rename and added Target property docs
Microsoft.Maui-vscode.sln Added SourceGen.UnitTests project to the solution
Comments suppressed due to low confidence (3)

src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs:536

  • Operator precedence makes this evaluate as (A && B) || C, causing the global xmlns check to bypass the DataTemplate name check. Wrap the namespace conditions in parentheses: && (B || C).
if (n.XmlType.Name == nameof(Microsoft.Maui.Controls.DataTemplate)
    && (n.XmlType.NamespaceUri == XamlParser.MauiUri) || n.XmlType.NamespaceUri == XamlParser.MauiGlobalUri)

src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs:96

  • [nitpick] Remove this commented-out code block if it's no longer needed to keep the codebase clean.
// if (!(parent.Parent is IElementNode target) || target.XmlType.NamespaceUri != XamlParser.MauiUri || target.XmlType.Name != nameof(VisualStateGroup))

src/Controls/docs/Microsoft.Maui.Controls/XmlnsDefinitionAttribute.xml:30

  • The F# usage example still references 'clrNamespace' but the constructor parameter was renamed to 'target'. Update the usage to 'new XmlnsDefinitionAttribute (xmlNamespace, target)'.
<MemberSignature Language="F#" Value="new Microsoft.Maui.Controls.XmlnsDefinitionAttribute : string * string -> Microsoft.Maui.Controls.XmlnsDefinitionAttribute" Usage="new Microsoft.Maui.Controls.XmlnsDefinitionAttribute (xmlNamespace, clrNamespace)" />

@@ -10,10 +10,14 @@
<IsPackable>false</IsPackable>
<DisableMSBuildAssemblyCopyCheck>true</DisableMSBuildAssemblyCopyCheck>
<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>
<DefineConstants>$(DefineConstants);MauiAllowImplicitXmlnsDeclaration</DefineConstants>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those 2 lines enable the preview features

<DefineConstants>$(DefineConstants);MauiAllowImplicitXmlnsDeclaration</DefineConstants>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but those 2 are only there to help debugging

@@ -86,19 +92,19 @@ static string FindTypeNameForVisualState(IElementNode parent, IXmlLineInfo lineI
//1. parent is VisualState, don't check that

//2. check that the VS is in a VSG
if (!(parent.Parent is IElementNode target) || target.XmlType.NamespaceUri != XamlParser.MauiUri || target.XmlType.Name != nameof(VisualStateGroup))
// if (!(parent.Parent is IElementNode target) || target.XmlType.NamespaceUri != XamlParser.MauiUri || target.XmlType.Name != nameof(VisualStateGroup))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// if (!(parent.Parent is IElementNode target) || target.XmlType.NamespaceUri != XamlParser.MauiUri || target.XmlType.Name != nameof(VisualStateGroup))

Comment on lines +173 to +184
var nsmgr = new XmlNamespaceManager(new NameTable());
nsmgr.AddNamespace("__f__", XamlParser.MauiUri);
nsmgr.AddNamespace("__g__", XamlParser.MauiGlobalUri);
if (allowImplicitXmlns)
{
nsmgr.AddNamespace("", XamlParser.DefaultImplicitUri);
foreach (var xmlnsPrefix in xmlnsCache.XmlnsPrefixes)
nsmgr.AddNamespace(xmlnsPrefix.Prefix, xmlnsPrefix.XmlNamespace);
}
using var reader = XmlReader.Create(new StringReader(text.ToString()),
new XmlReaderSettings { ConformanceLevel = allowImplicitXmlns ? ConformanceLevel.Fragment : ConformanceLevel.Document },
new XmlParserContext(nsmgr.NameTable, nsmgr, null, XmlSpace.None));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a non-trivial piece of code and there are quite a few copies of it here and in the XamlC code. Would it make sense to move it to a separate method for consistency sake?


Assert.IsTrue(result1.TrackedSteps.All(s => s.Value.Single().Outputs.Single().Reason == IncrementalStepRunReason.New));
// Assert.IsTrue(result1.TrackedSteps.All(s => s.Value.Single().Outputs.Single().Reason == IncrementalStepRunReason.New));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this assert commented out?

{
var xaml =
"""
<?xml version="1.0" encoding="UTF-8"?>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML declaration is optional or mandatory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when your file encoding is "correct", it can be skipped

using Microsoft.Maui.Controls;
using NUnit.Framework;

[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "Microsoft.Maui.Controls.Xaml.UnitTests.NSGlobalXmlnsWithXStatic")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused now that I see the usage of this attribute. Do we expect the first parameter to be "http://schemas.microsoft.com/dotnet/maui/global" in most cases? If yes, we could simplify this by having something like [assembly: GlobalXmlnsDefinition("MyNamespace")]?

Comment on lines +7 to +8
[assembly: XmlnsDefinition("http://companyone.com/schemas/toolkit", "CompanyOne.Controls")]
[assembly: XmlnsPrefix("c1", "http://companyone.com/schemas/toolkit")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it expected to use these two together? Would it be possible to allow the developer to completely skip declaring the URL and just do something like [assembly: XmlnsPrefix("c1", "CompanyOne.Controls")]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are often multiple XmlnsDefinitions for a single XmlnsPrefix. I'm worried that the apparent simplification will cause confusion

@@ -388,7 +388,7 @@ public void DesignTimeBuild()
var xamlCStamp = IOPath.Combine(intermediateDirectory, "XamlC.stamp");

//The assembly should not be compiled
AssertDoesNotExist(assembly);
//AssertDoesNotExist(assembly);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this assert commented out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cause there are now multiple source gen contributing to an assembly being created

Copy link
Member

@rmarinho rmarinho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems build is failing


/mnt/vss/_work/1/s/src/Controls/src/Build.Tasks/TypeReferenceExtensions.cs(28,8): error CS0103: The name 'IsSystemAssemvly' does not exist in the current context [/mnt/vss/_work/1/s/src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj]
/mnt/vss/_work/1/s/src/Controls/src/Build.Tasks/TypeReferenceExtensions.cs(28,34): error CS0103: The name 'IsSystemAssemvly' does not exist in the current context [/mnt/vss/_work/1/s/src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj]
/mnt/vss/_work/1/s/src/Controls/src/Build.Tasks/TypeReferenceExtensions.cs(36,8): error CS0103: The name 'IsSystemAssemvly' does not exist in the current context [/mnt/vss/_work/1/s/src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj]
    0 Warning(s)
    3 Error(s)

@github-project-automation github-project-automation bot moved this from Ready To Review to Changes Requested in MAUI SDK Ongoing May 27, 2025
@simonrozsival
Copy link
Member

Using XmlnsDefinition and XmlnsPrefix seems just fine for libraries but I'd like to suggest simplifying the usage for app developers for declaring global usings for their CLR namespaces:

[assembly: XamlUsing("MyApp.ViewModels")]
[assembly: XamlUsing("MyApp.ViewModels", prefix: "vm")]

The optional prefix parameter could help customers get rid of xmlns:vm="clr-namespace:...;..." but keep the XAML code which already uses those prefixes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-xaml XAML, CSS, Triggers, Behaviors
Projects
Status: Changes Requested
4 participants