Part of #34099.
The XAML markup-extension forms {OnPlatform ...} and {OnIdiom ...} hardcode a closed, sealed set of platforms/idioms, so a third-party platform backend (e.g. Linux/GTK, or a Web/WASM backend) cannot express platform-conditional values in XAML the way first-party platforms can. The element forms (OnPlatform<T> / On) are already data-driven and work for custom platforms — so the inconsistency is purely in the markup extensions.
Problem
OnPlatformExtension (Microsoft.Maui.Controls.Xaml)
The platform values are fixed, named properties and several aren't even public:
public object Android { get; set; }
internal object GTK { get; set; } // internal!
public object iOS { get; set; }
internal object macOS { get; set; } // internal!
public object MacCatalyst { get; set; }
public object Tizen { get; set; }
public object UWP { get; set; } // [Obsolete]
internal object WPF { get; set; } // internal!
public object WinUI { get; set; }
Matching is a hardcoded if-chain rather than a data-driven lookup:
private bool TryGetValueForPlatform(out object value)
{
if (DeviceInfo.Platform == DevicePlatform.Android && Android != s_notset) { ... }
if (DeviceInfo.Platform == DevicePlatform.Create("GTK") && GTK != s_notset) { ... }
if (DeviceInfo.Platform == DevicePlatform.iOS && iOS != s_notset) { ... }
// ... macOS, MacCatalyst, Tizen, WinUI, UWP — fixed set, no extensibility
}
There is no way to add a value for a platform string the framework doesn't already know about (e.g. Web, Browser, Linux). Because OnPlatformExtension is a sealed-by-convention framework type with a fixed property set, it also can't be subclassed to add one.
OnIdiomExtension (Microsoft.Maui.Controls.Xaml)
Same shape — fixed properties (Phone, Tablet, Desktop, TV, Watch) and a hardcoded if-chain over DeviceIdiom. DeviceIdiom.Create(...) allows custom idioms at runtime, but the markup extension can't express them. (Lower priority — most backends can map to one of the existing five idioms — but it's the same class of problem.)
Contrast: the element form already works
OnPlatform<T> / On are fully data-driven and already support arbitrary platform strings:
public class On
{
public IList<string> Platform { get; set; } // arbitrary strings
}
// OnPlatform<T> implicit conversion:
else if (platform.Platform.Contains(DeviceInfo.Platform.ToString())) { ... }
So a custom backend can already do this today:
<View.Margin>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="Default" Value="0" />
<On Platform="Web" Value="0,0,0,20" />
</OnPlatform>
</View.Margin>
…as long as DeviceInfo.Platform.ToString() returns the custom string (see the DeviceInfo dependency below). This proves the matching can be data-driven without breaking first-party platforms — the markup extensions just haven't been generalized.
Proposed solutions (any one would close the gap)
- Make matching data-driven in the markup extensions. Resolve the value by comparing
DeviceInfo.Platform.ToString() (and DeviceInfo.Idiom.ToString()) against a case-insensitive key, the same way the element form does. Keep the existing named properties as sugar that populate the same dictionary, so first-party usage is unchanged.
- Allow arbitrary keys via an indexer / content collection on
OnPlatformExtension (e.g. accept <On Platform="Web">-style children, or a string-keyed dictionary), so unknown platforms round-trip.
- At minimum: make
GTK/macOS/WPF public and document the element form as the supported route for non-first-party platforms, and ensure the docs/analyzers don't steer people exclusively to the markup-extension form.
DeviceInfo dependency
Both forms ultimately read the static DeviceInfo.Platform / DeviceInfo.Idiom. For a custom backend these resolve to DevicePlatform.Unknown unless the backend installs its own IDeviceInfo via the internal DeviceInfo.SetCurrent(IDeviceInfo) (reflection workaround today). That registration gap is tracked by #34100 (which covers Microsoft.Maui.Devices); this issue is specifically about the XAML markup extensions' closed value set, which remains a gap even after DeviceInfo is registrable.
What we're NOT asking for
- Not asking MAUI to officially support any new platform.
- Not asking to change the first-party named properties' behavior.
Just asking the markup extensions to stop hardcoding a closed set, so community backends get the same XAML ergonomics first-party platforms already enjoy (and that the element form already provides).
Environment
- .NET 10
- Custom backends:
Maui.Gtk (Linux), and a Web/WASM GDUI backend
- Assemblies inspected:
Microsoft.Maui.Controls.Xaml 10.0.60 (OnPlatformExtension, OnIdiomExtension), Microsoft.Maui.Controls.Core 10.0.60 (OnPlatform<T>, On)
Part of #34099.
The XAML markup-extension forms
{OnPlatform ...}and{OnIdiom ...}hardcode a closed, sealed set of platforms/idioms, so a third-party platform backend (e.g. Linux/GTK, or a Web/WASM backend) cannot express platform-conditional values in XAML the way first-party platforms can. The element forms (OnPlatform<T>/On) are already data-driven and work for custom platforms — so the inconsistency is purely in the markup extensions.Problem
OnPlatformExtension(Microsoft.Maui.Controls.Xaml)The platform values are fixed, named properties and several aren't even public:
Matching is a hardcoded
if-chain rather than a data-driven lookup:There is no way to add a value for a platform string the framework doesn't already know about (e.g.
Web,Browser,Linux). BecauseOnPlatformExtensionis a sealed-by-convention framework type with a fixed property set, it also can't be subclassed to add one.OnIdiomExtension(Microsoft.Maui.Controls.Xaml)Same shape — fixed properties (
Phone,Tablet,Desktop,TV,Watch) and a hardcodedif-chain overDeviceIdiom.DeviceIdiom.Create(...)allows custom idioms at runtime, but the markup extension can't express them. (Lower priority — most backends can map to one of the existing five idioms — but it's the same class of problem.)Contrast: the element form already works
OnPlatform<T>/Onare fully data-driven and already support arbitrary platform strings:So a custom backend can already do this today:
…as long as
DeviceInfo.Platform.ToString()returns the custom string (see theDeviceInfodependency below). This proves the matching can be data-driven without breaking first-party platforms — the markup extensions just haven't been generalized.Proposed solutions (any one would close the gap)
DeviceInfo.Platform.ToString()(andDeviceInfo.Idiom.ToString()) against a case-insensitive key, the same way the element form does. Keep the existing named properties as sugar that populate the same dictionary, so first-party usage is unchanged.OnPlatformExtension(e.g. accept<On Platform="Web">-style children, or a string-keyed dictionary), so unknown platforms round-trip.GTK/macOS/WPFpublic and document the element form as the supported route for non-first-party platforms, and ensure the docs/analyzers don't steer people exclusively to the markup-extension form.DeviceInfo dependency
Both forms ultimately read the static
DeviceInfo.Platform/DeviceInfo.Idiom. For a custom backend these resolve toDevicePlatform.Unknownunless the backend installs its ownIDeviceInfovia the internalDeviceInfo.SetCurrent(IDeviceInfo)(reflection workaround today). That registration gap is tracked by #34100 (which coversMicrosoft.Maui.Devices); this issue is specifically about the XAML markup extensions' closed value set, which remains a gap even afterDeviceInfois registrable.What we're NOT asking for
Just asking the markup extensions to stop hardcoding a closed set, so community backends get the same XAML ergonomics first-party platforms already enjoy (and that the element form already provides).
Environment
Maui.Gtk(Linux), and a Web/WASM GDUI backendMicrosoft.Maui.Controls.Xaml10.0.60 (OnPlatformExtension,OnIdiomExtension),Microsoft.Maui.Controls.Core10.0.60 (OnPlatform<T>,On)