.NET MAUI:我们还可以拥有特定于平台的 XAML 吗?

Lil*_*lly 7 xaml maui

我读到,我们可以在 MAUI 项目中使用特定于平台的 C#,但我需要使用特定于平台的 XAML 文件。在文档中找不到任何内容。

背景: 在 Android 上,当从绑定的 ObservableCollection 中删除项目时,ListView 内的 SwipeView 会引发 Java 错误(“指定的子项已经有父项。您必须首先在子项的父项上调用removeView()。”),因此我使用CollectionView 在这里(没有错误)。在 iOS 上,当与页面上的其他元素一起使用时,CollectionView 确实会延伸到页面下端之外(看起来,在 iOS 上 CollectionView 确实想要占据整个屏幕高度),所以我必须在这里使用 ListView。但在 iOS 上,不会发生上述错误。

Jul*_*ian 15

您可以通过针对每个平台的单独 XAML 文件来实现此目的。只需编写两个不同版本的 XAML,一种用于 iOS,一种用于 Android。然后,在运行时,根据应用程序运行的系统实例化正确的实例。

页数

对于整个页面,您可以只为 Android 创建一个页面版本,为 iOS 创建一个版本(如果需要,还可以分别为其余平台创建其他版本)。

安卓页面

我们将 Android 页面命名为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>
Run Code Online (Sandbox Code Playgroud)

iOS 页面

iOS 版本将被称为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>
Run Code Online (Sandbox Code Playgroud)

打开特定于平台的页面

您可以决定在运行时打开哪个版本,也可以使用多目标/条件编译。您可以从后面的代码执行此操作,例如MainPage.xaml.cs

运行时决定

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

条件编译

private async void OpenHelloView(object sender, EventArgs e)
{
#if ANDROID
    await Navigation.PushAsync(new HelloFromAndroid());
#elif IOS
    await Navigation.PushAsync(new HelloFromiOS());
#endif
}
Run Code Online (Sandbox Code Playgroud)

此版本是首选方式,因为编译后的代码仅包含所需的特定于平台的调用。

意见

对于视图来说,这有点复杂,但也是可能的。

如果您需要特定于平台的视图,则无法将其添加到页面的 XAML,而是需要在运行时从代码隐藏动态实例化它,并将其作为子视图添加到另一个视图或布局。

安卓视图

让我们创建一个名为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>
Run Code Online (Sandbox Code Playgroud)

iOS 视图

我们还创建一个名为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>
Run Code Online (Sandbox Code Playgroud)

具有特定于平台的视图的页面

现在,让我们创建一个使用特定于平台的视图的页面。首先,我们创建一些 XAML。为简单起见,我使用一个空的VerticalStackLayout,并使用x:Name属性命名:

<?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>
Run Code Online (Sandbox Code Playgroud)

VerticalLayout现在,我可以从代码隐藏中访问该字段并向其中添加子元素。这可以再次通过运行时决策或通过条件编译动态完成。

运行时决定

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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

条件编译

public partial class PageWithPlatformSpecificView : ContentPage
{
    public PageWithPlatformSpecificView()
    {
        InitializeComponent();

#if ANDROID
        VerticalLayout.Add(new ViewAndroid());
#elif IOS
        VerticalLayout.Add(new ViewiOS());
#endif
    }
}
Run Code Online (Sandbox Code Playgroud)

同样,条件编译是首选方式。

替代方法

仅 XAML 使用<OnPlatform>

您也可以在 XAML 中执行此操作,但请注意,此方法将包括应用程序包中所有平台的所有视图,例如:

<ContentView>
    <OnPlatform x:TypeArguments="View">
        <On Platform="Android">
            <android:ViewAndroid />
        </On>
        <On Platform="iOS">
            <ios:ViewiOS />
        </On>
    </OnPlatform>
</ContentView>
Run Code Online (Sandbox Code Playgroud)

<OnIdiom>对于其他标记也同样适用。

多目标

从技术上讲,您还可以使用基于文件名的多目标来仅在最终版本中包含正确版本的 XAML。

但是,它需要在.csproj文件中进行复杂的设置才能为 XAML 文件配置多目标,而这并未得到官方支持。我在这里为处理特定于平台的资源字典的类似问题写了一个答案: https: //stackoverflow.com/a/74338355/4308455

概括

正如您所看到的,使用特定于平台的 XAML 可以通过不同的方法和可能性来实现此目的。它与特定于平台的 C# 代码并不完全相同,但它对于某些场景很有用。我希望这对您和有类似要求的其他人有所帮助。

如果您有兴趣,我自己尝试过并将代码添加到我的MAUI 示例存储库中。