diff --git a/src/Controls/src/Xaml/MarkupExtensions/OnPlatformExtension.cs b/src/Controls/src/Xaml/MarkupExtensions/OnPlatformExtension.cs
index 5d8d080e6ca7..286a86ce704a 100644
--- a/src/Controls/src/Xaml/MarkupExtensions/OnPlatformExtension.cs
+++ b/src/Controls/src/Xaml/MarkupExtensions/OnPlatformExtension.cs
@@ -10,6 +10,32 @@ namespace Microsoft.Maui.Controls.Xaml
///
/// Provides a XAML markup extension that returns different values depending on the platform the app is running on.
///
+ ///
+ /// The value is resolved by matching the identifier
+ /// (the struct's ToString() value)
+ /// against the per-platform values, so a custom backend whose platform string matches one of the named
+ /// properties below (for example GTK) resolves correctly at runtime. The match is
+ /// case-sensitive (ordinal, per equality), so the
+ /// identifier must match the property name's casing exactly — a backend reporting
+ /// DevicePlatform.Create("gtk") (lowercase) does not match the GTK property and falls through
+ /// to . Note that the compile-time
+ /// optimization (SimplifyOnPlatformVisitor) currently recognizes only the
+ /// -android/-ios/-macos/-maccatalyst target frameworks; every other target
+ /// framework (including -windows and -tizen) and any custom backend falls back to runtime
+ /// resolution.
+ ///
+ /// Because a XAML markup extension maps each named argument to a CLR property, the inline
+ /// {OnPlatform iOS=…, Android=…} form can only express the platforms exposed as properties here.
+ /// To target an arbitrary custom platform (e.g. a backend reporting
+ /// DevicePlatform.Create("Web")), use the element form, which accepts any platform string:
+ ///
+ /// <OnPlatform x:TypeArguments="x:String">
+ /// <On Platform="Web" Value="…" />
+ /// <On Platform="iOS, Android" Value="…" />
+ /// </OnPlatform>
+ ///
+ ///
+ ///
[ContentProperty(nameof(Default))]
[RequireService(
[typeof(IProvideValueTarget),
@@ -31,14 +57,21 @@ public class OnPlatformExtension : IMarkupExtension
///
public object Android { get; set; } = s_notset;
- internal object GTK { get; set; } = s_notset;
+ ///
+ /// Gets or sets the value to use on GTK.
+ ///
+ public object GTK { get; set; } = s_notset;
///
/// Gets or sets the value to use on iOS.
///
public object iOS { get; set; } = s_notset;
- internal object macOS { get; set; } = s_notset;
+ ///
+ /// Gets or sets the value to use on macOS.
+ ///
+ /// Note, this is different than .
+ public object macOS { get; set; } = s_notset;
///
/// Gets or sets the value to use on Mac Catalyst.
@@ -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;
+ ///
+ /// Gets or sets the value to use on WPF.
+ ///
+ public object WPF { get; set; } = s_notset;
///
/// Gets or sets the value to use on Windows (WinUI).
diff --git a/src/Controls/src/Xaml/PublicAPI/net-android/PublicAPI.Unshipped.txt b/src/Controls/src/Xaml/PublicAPI/net-android/PublicAPI.Unshipped.txt
index a5c1d1fa583f..91fc8305068a 100644
--- a/src/Controls/src/Xaml/PublicAPI/net-android/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Xaml/PublicAPI/net-android/PublicAPI.Unshipped.txt
@@ -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?
diff --git a/src/Controls/src/Xaml/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Xaml/PublicAPI/net-ios/PublicAPI.Unshipped.txt
index a5c1d1fa583f..91fc8305068a 100644
--- a/src/Controls/src/Xaml/PublicAPI/net-ios/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Xaml/PublicAPI/net-ios/PublicAPI.Unshipped.txt
@@ -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?
diff --git a/src/Controls/src/Xaml/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Xaml/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
index a5c1d1fa583f..91fc8305068a 100644
--- a/src/Controls/src/Xaml/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Xaml/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
@@ -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?
diff --git a/src/Controls/src/Xaml/PublicAPI/net-tizen/PublicAPI.Unshipped.txt b/src/Controls/src/Xaml/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
index a5c1d1fa583f..91fc8305068a 100644
--- a/src/Controls/src/Xaml/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Xaml/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
@@ -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?
diff --git a/src/Controls/src/Xaml/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Controls/src/Xaml/PublicAPI/net-windows/PublicAPI.Unshipped.txt
index a5c1d1fa583f..91fc8305068a 100644
--- a/src/Controls/src/Xaml/PublicAPI/net-windows/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Xaml/PublicAPI/net-windows/PublicAPI.Unshipped.txt
@@ -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?
diff --git a/src/Controls/src/Xaml/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Controls/src/Xaml/PublicAPI/net/PublicAPI.Unshipped.txt
index 79c24c9732e0..7221188e52c3 100644
--- a/src/Controls/src/Xaml/PublicAPI/net/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Xaml/PublicAPI/net/PublicAPI.Unshipped.txt
@@ -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?
diff --git a/src/Controls/src/Xaml/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Controls/src/Xaml/PublicAPI/netstandard/PublicAPI.Unshipped.txt
index 267609aa29ae..f74b7d2db606 100644
--- a/src/Controls/src/Xaml/PublicAPI/netstandard/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Xaml/PublicAPI/netstandard/PublicAPI.Unshipped.txt
@@ -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?
diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs
index b0de84267be5..1d3b636ea584 100644
--- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs
+++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs
@@ -135,6 +135,47 @@ private partial void InitializeComponent()
Assert.Equal(expected, generated, ignoreLineEndingDifferences: true);
}
+ [Fact]
+ public void NonSimplifiedOnPlatformMarkupExtensionUsesPublicCustomPlatformProperties()
+ {
+ var xaml =
+"""
+
+
+
+
+""";
+
+ 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()
{
diff --git a/src/Controls/tests/Xaml.UnitTests/MarkupExpressionParserTests.cs b/src/Controls/tests/Xaml.UnitTests/MarkupExpressionParserTests.cs
index c9e950bfcdff..fab85ab9a407 100644
--- a/src/Controls/tests/Xaml.UnitTests/MarkupExpressionParserTests.cs
+++ b/src/Controls/tests/Xaml.UnitTests/MarkupExpressionParserTests.cs
@@ -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)]
diff --git a/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs b/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs
index 8db78cec8d95..bd2f4cbc8280 100644
--- a/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs
+++ b/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs
@@ -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]
+ [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 = @"
+ ";
+
+ mockDeviceInfo.Platform = DevicePlatform.Create(platform);
+ var label = new Label().LoadFromXaml(xaml);
+ Assert.Equal(expected, label.Text);
+ }
}
[Collection("Xaml Inflation")]