Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
296c9dd
[XAML] Make OnPlatform/OnIdiom markup extensions data-driven
Copilot Jun 12, 2026
e328453
Address review: use Ordinal (case-sensitive) platform/idiom matching
Copilot Jun 12, 2026
7a59777
Address round-3 review: zero-allocation platform/idiom matching
Copilot Jun 13, 2026
ea9176f
Merge remote-tracking branch 'upstream/net11.0' into fix/35695-onplat…
kubaflo Jun 24, 2026
259c233
Document the element form for arbitrary custom platforms in OnPlatform
kubaflo Jun 24, 2026
c38372a
Merge remote-tracking branch 'origin/net11.0' into fix/35695-onplatfo…
Copilot Jul 7, 2026
ce09b16
Clarify OnPlatformExtension remarks: DeviceInfo.Platform is a DeviceP…
Copilot Jul 7, 2026
63b0f00
Correct OnPlatform remarks: custom backends resolve at runtime only
Copilot Jul 7, 2026
aceb41e
Clarify OnIdiom custom-idiom comment: no data-driven element form
Copilot Jul 18, 2026
03034e8
Drop inaccurate reference-equality claim in OnIdiom comment
Copilot Jul 18, 2026
82ec583
Clarify OnPlatform remarks: -windows/-tizen use runtime resolution
Copilot Jul 18, 2026
18b4b06
Limit OnPlatform extensibility fix to public API
Copilot Jul 18, 2026
ddc61e8
Merge remote-tracking branch 'origin/net11.0' into kubaflo/pr-35901-m…
Copilot Jul 18, 2026
5725042
Merge net11.0 into XAML extensibility branch
Copilot Jul 20, 2026
c5b624a
Test all custom OnPlatform markup values
Copilot Jul 21, 2026
cdb674e
Add SourceGen coverage for custom OnPlatform values
Copilot Jul 21, 2026
8669744
Document that OnPlatform property matching is case-sensitive (#35901 …
kubaflo Jul 27, 2026
87941ab
Merge branch 'net11.0' into fix/35695-onplatform-onidiom-data-driven
kubaflo Jul 28, 2026
cc58dc2
Merge net11.0 into PR #35901
Copilot Jul 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions src/Controls/src/Xaml/MarkupExtensions/OnPlatformExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ namespace Microsoft.Maui.Controls.Xaml
/// <summary>
/// Provides a XAML markup extension that returns different values depending on the platform the app is running on.
/// </summary>
/// <remarks>
/// The value is resolved by matching the <see cref="Microsoft.Maui.Devices.DeviceInfo.Platform"/> identifier
/// (the <see cref="Microsoft.Maui.Devices.DevicePlatform"/> struct's <c>ToString()</c> value)
/// against the per-platform values, so a custom backend whose platform string matches one of the named
Comment thread
kubaflo marked this conversation as resolved.
/// properties below (for example <c>GTK</c>) resolves correctly at runtime. The match is
/// <em>case-sensitive</em> (ordinal, per <see cref="Microsoft.Maui.Devices.DevicePlatform"/> equality), so the
/// identifier must match the property name's casing exactly — a backend reporting
/// <c>DevicePlatform.Create("gtk")</c> (lowercase) does not match the <c>GTK</c> property and falls through
/// to <see cref="Default"/>. Note that the compile-time
/// optimization (<c>SimplifyOnPlatformVisitor</c>) currently recognizes only the
/// <c>-android</c>/<c>-ios</c>/<c>-macos</c>/<c>-maccatalyst</c> target frameworks; every other target
/// framework (including <c>-windows</c> and <c>-tizen</c>) and any custom backend falls back to runtime
/// resolution.
/// <para>
/// Because a XAML markup extension maps each named argument to a CLR property, the inline
/// <c>{OnPlatform iOS=…, Android=…}</c> form can only express the platforms exposed as properties here.
/// To target an <em>arbitrary</em> custom platform (e.g. a backend reporting
/// <c>DevicePlatform.Create("Web")</c>), use the element form, which accepts any platform string:
/// <code>
/// &lt;OnPlatform x:TypeArguments="x:String"&gt;
/// &lt;On Platform="Web" Value="…" /&gt;
/// &lt;On Platform="iOS, Android" Value="…" /&gt;
/// &lt;/OnPlatform&gt;
/// </code>
/// </para>
/// </remarks>
[ContentProperty(nameof(Default))]
[RequireService(
[typeof(IProvideValueTarget),
Expand All @@ -31,14 +57,21 @@ public class OnPlatformExtension : IMarkupExtension
/// </summary>
public object Android { get; set; } = s_notset;

internal object GTK { get; set; } = s_notset;
/// <summary>
/// Gets or sets the value to use on GTK.
/// </summary>
public object GTK { get; set; } = s_notset;

/// <summary>
/// Gets or sets the value to use on iOS.
/// </summary>
public object iOS { get; set; } = s_notset;

internal object macOS { get; set; } = s_notset;
/// <summary>
/// Gets or sets the value to use on macOS.
/// </summary>
/// <remarks>Note, this is different than <see cref="MacCatalyst"/>.</remarks>
public object macOS { get; set; } = s_notset;

/// <summary>
/// Gets or sets the value to use on Mac Catalyst.
Expand All @@ -56,7 +89,10 @@ public class OnPlatformExtension : IMarkupExtension
[Obsolete("Use WinUI instead.")]
public object UWP { get; set; } = s_notset;

internal object WPF { get; set; } = s_notset;
/// <summary>
/// Gets or sets the value to use on WPF.
/// </summary>
public object WPF { get; set; } = s_notset;

/// <summary>
/// Gets or sets the value to use on Windows (WinUI).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.get -> System.Globalization.CultureInfo
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.set -> void
Microsoft.Maui.Controls.Xaml.XamlComponentRegistry
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.FindStaticResource(object! element, string! key) -> object?
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.GetComponent(object! page, string! nodeId) -> object?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.get -> System.Globalization.CultureInfo
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.set -> void
Microsoft.Maui.Controls.Xaml.XamlComponentRegistry
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.FindStaticResource(object! element, string! key) -> object?
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.GetComponent(object! page, string! nodeId) -> object?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.get -> System.Globalization.CultureInfo
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.set -> void
Microsoft.Maui.Controls.Xaml.XamlComponentRegistry
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.FindStaticResource(object! element, string! key) -> object?
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.GetComponent(object! page, string! nodeId) -> object?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.get -> System.Globalization.CultureInfo
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.set -> void
Microsoft.Maui.Controls.Xaml.XamlComponentRegistry
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.FindStaticResource(object! element, string! key) -> object?
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.GetComponent(object! page, string! nodeId) -> object?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.get -> System.Globalization.CultureInfo
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.set -> void
Microsoft.Maui.Controls.Xaml.XamlComponentRegistry
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.FindStaticResource(object! element, string! key) -> object?
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.GetComponent(object! page, string! nodeId) -> object?
Expand Down
6 changes: 6 additions & 0 deletions src/Controls/src/Xaml/PublicAPI/net/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.get -> System.Globalization.CultureInfo
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.set -> void
Microsoft.Maui.Controls.Xaml.XamlComponentRegistry
Microsoft.Maui.Controls.Xaml.XamlIncrementalHotReloadHandler
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.FindStaticResource(object! element, string! key) -> object?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.get -> System.Globalization.CultureInfo
~Microsoft.Maui.Controls.Xaml.BindingExtension.ConverterCulture.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.GTK.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.WPF.set -> void
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.get -> object
~Microsoft.Maui.Controls.Xaml.OnPlatformExtension.macOS.set -> void
Microsoft.Maui.Controls.Xaml.XamlComponentRegistry
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.FindStaticResource(object! element, string! key) -> object?
static Microsoft.Maui.Controls.Xaml.XamlComponentRegistry.GetComponent(object! page, string! nodeId) -> object?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,47 @@ private partial void InitializeComponent()
Assert.Equal(expected, generated, ignoreLineEndingDifferences: true);
}

[Fact]
public void NonSimplifiedOnPlatformMarkupExtensionUsesPublicCustomPlatformProperties()
{
var xaml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Test.TestPage">
<Label Text="{OnPlatform Default=default, GTK=gtk, macOS=macos, WPF=wpf}" />
</ContentPage>
""";

var code =
"""
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;

namespace Test;

[XamlProcessing(XamlInflator.SourceGen)]
public partial class TestPage : ContentPage
{
public TestPage()
{
InitializeComponent();
}
}
""";

var (result, generated) = RunGenerator(xaml, code, targetFramework: "netstandard2.0");
Assert.False(result.Diagnostics.Any());

Assert.Contains("global::Microsoft.Maui.Controls.Xaml.OnPlatformExtension", generated, StringComparison.Ordinal);
Assert.Matches(@"GTK\s*=\s*""gtk""", generated);
Assert.Matches(@"macOS\s*=\s*""macos""", generated);
Assert.Matches(@"WPF\s*=\s*""wpf""", generated);
Assert.Contains("ProvideValue(xamlServiceProvider)", generated, StringComparison.Ordinal);
}

[Fact]
public void SimplifyOnPlatformElement()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ public void BindingWithStaticConverter()
[InlineData("{OnPlatform Android=20, UWP=25}", "WinUI", 25)]
[InlineData("{OnPlatform Android=20, WinUI=25, UWP=20}", "WinUI", 25)]
[InlineData("{OnPlatform Android=20, UWP=25}", "UWP", 25)]
// GTK, macOS and WPF are now public, so custom/community backends can express
// platform-conditional values for them through the {OnPlatform} markup extension.
[InlineData("{OnPlatform Android=20, GTK=25}", "GTK", 25)]
[InlineData("{OnPlatform iOS=20, macOS=25}", "macOS", 25)]
[InlineData("{OnPlatform Android=20, WPF=25}", "WPF", 25)]
[InlineData("{OnPlatform 20}", "Android", 20)]
[InlineData("{OnPlatform 20}", "iOS", 20)]
[InlineData("{OnPlatform 20}", "Tizen", 20)]
Expand Down
18 changes: 18 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,24 @@ public void ChecksPreferWinUI()
image = new Image().LoadFromXaml(xaml);
Assert.Equal("Images/icon_twitter_preferred.png", (image.Source as FileImageSource).File);
}

[Theory]
Comment thread
kubaflo marked this conversation as resolved.
[InlineData("GTK", "gtk")]
[InlineData("macOS", "macos")]
[InlineData("WPF", "wpf")]
// Issue 35695: custom/community backends can express platform-conditional
// values through the OnPlatform markup-extension form.
public void MarkupExtensionResolvesCustomPlatform(string platform, string expected)
{
var xaml = @"
<Label xmlns=""http://schemas.microsoft.com/dotnet/2021/maui""
xmlns:x=""http://schemas.microsoft.com/winfx/2009/xaml""
Text=""{OnPlatform Default=default, GTK=gtk, macOS=macos, WPF=wpf}"" />";

mockDeviceInfo.Platform = DevicePlatform.Create(platform);
var label = new Label().LoadFromXaml(xaml);
Assert.Equal(expected, label.Text);
}
}

[Collection("Xaml Inflation")]
Expand Down
Loading