I read, that we can use platform-specific C# in MAUI projects, but I have the need to use a platform-specific XAML file. Couldn't find anything in the docs.
Background: On Android a SwipeView inside a ListView throws a Java error when removing an item from the bound ObservableCollection ("The specified child already has a parent. You must call removeView() on the child's parent first."), so I use a CollectionView here (no error). On iOS, the CollectionView does extend outside the lower end of the page, when used together with other elements on a page (it seems, on iOS a CollectionView does want to take the full screen height), so I have to use ListView here. On iOS though, the above mentioned error does not occur.
CodePudding user response:
You can achieve this with separate XAML files for each platform. Just write two different versions of your XAML, one for iOS and one for Android. Then, during runtime, instantiate the right one based on the system your application is running on.
Pages
For entire pages, you could just create one version of the page for Android and one for iOS (and other ones for the remaining platforms respectively, if needed).
Android Page
Let's call the Android page HelloFromAndroid.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="MauiSamples.Views.Platform.HelloFromAndroid"
Title="HelloFromAndroid"
Shell.PresentationMode="Modal">
<VerticalStackLayout>
<Label
Text="Hello from Android!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
iOS Page
The iOS version will be called HelloFromiOS.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="MauiSamples.Views.Platform.HelloFromiOS"
Title="HelloFromiOS"
Shell.PresentationMode="Modal">
<VerticalStackLayout>
<Label
Text="Hello from iOS!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
Opening the platform-specific pages
You can either decide which version to open during runtime, or you can use multi-targeting/conditional compilation. You do this from your code behind, e.g. the MainPage.xaml.cs:
Runtime decision
private async void OpenHelloView(object sender, EventArgs e)
{
if (DeviceInfo.Platform == DevicePlatform.Android)
{
await Navigation.PushAsync(new HelloFromAndroid());
}
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
await Navigation.PushAsync(new HelloFromiOS());
}
}
Conditional compilation
private async void OpenHelloView(object sender, EventArgs e)
{
#if ANDROID
await Navigation.PushAsync(new HelloFromAndroid());
#elif IOS
await Navigation.PushAsync(new HelloFromiOS());
#endif
}
This version is the preferred way, because the compiled code only contains the required platform-specific call.
Views
For Views, this is a little more complicated, but also possible.
If you need a platform-specific View, you cannot add it to your page's XAML and instead need to instantiate it dynamically during runtime from the code-behind and add it as a child to another View or a Layout.
Android View
Let's create a View called ViewAndroid:
XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSamples.Views.Platform.ViewAndroid">
<Label
Text="Hello from Android!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentView>
iOS View
And let's also create a View called ViewiOS:
XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSamples.Views.Platform.ViewiOS">
<Label
Text="Hello from iOS!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentView>
Page with platform-specific View
Now, let's create a Page that consumes the platform-specific Views. First, we create some XAML. For simplicity, I'm using an empty VerticalStackLayout
that I give a name using the x:Name
property:
<?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="MauiSamples.Views.Platform.PageWithPlatformSpecificView"
Title="PageWithPlatformSpecificView"
Shell.PresentationMode="Modal">
<VerticalStackLayout x:Name="VerticalLayout" />
</ContentPage>
Now, I can access the VerticalLayout
field from my code-behind and add child elements to it. This can again be done dynamically either via a runtime decision or via conditional compilation.
Runtime decision
public partial class PageWithPlatformSpecificView : ContentPage
{
public PageWithPlatformSpecificView()
{
InitializeComponent();
if (DeviceInfo.Platform == DevicePlatform.Android)
{
VerticalLayout.Add(new ViewAndroid());
}
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
VerticalLayout.Add(new ViewiOS());
}
}
}
Conditional compilation
public partial class PageWithPlatformSpecificView : ContentPage
{
public PageWithPlatformSpecificView()
{
InitializeComponent();
#if ANDROID
VerticalLayout.Add(new ViewAndroid());
#elif IOS
VerticalLayout.Add(new ViewiOS());
#endif
}
}
Again, the conditional compilation is the preferred way.
Alternative approaches
Technically, you could also use filename-based multi-targeting to only include the right version of the XAML in the final build.
However, it requires a complex setup in the .csproj file to configure multi-targeting for XAML files, which is not officially supported. I have written an answer on this for a similar problem that deals with platform-specific resource dictionaries here: https://stackoverflow.com/a/74338355/4308455
Summary
As you can see, there are different approaches and possibilities to accomplish this with platform-specific XAML. It's not exactly the same as platform-specific C# code, but it can be useful for certain scenarios. I hope this helps you and anyone else with similar requirements.
I've tried this myself and added the code to my MAUI samples repository, if you're interested.