.NET MAUI for .NET 9 中的新增功能

.NET 9 中的 .NET Multi-platform App UI (.NET MAUI) 侧重于提升产品质量。 这包括扩展测试覆盖范围、端到端方案测试和 bug 修复。 若要详细了解 .NET MAUI 9 中的产品质量提升情况,请参阅以下发行说明:

重要

由于使用外部依赖项(例如 Xcode 或 Android SDK Tools),.NET MAUI 支持策略与 .NET 和 .NET Core 支持策略不同。 有关详细信息,请参阅 .NET MAUI 支持策略

在 .NET 9 中,.NET MAUI 作为 .NET 工作负载和多个 NuGet 包提供。 这种方法的优势在于,它使你能够轻松地将项目固定到特定版本,同时使你能够轻松预览未发布的版本或实验性版本。 创建新的 .NET MAUI 项目时,所需的 NuGet 包会自动添加到项目中。

新控件

.NET MAUI 9 包括两个新控件。

HybridWebView

HybridWebView 支持在 Web 视图中托管任意 HTML/JS/CSS 内容,并允许 Web 视图 (JavaScript) 中的代码与托管 Web 视图 (C#/.NET) 的代码之间进行通信。 例如,如果已有 React JS 应用,则可以将其托管在跨平台 .NET MAUI 本机应用中,并使用 C# 和 .NET 生成应用的后端。

若要使用 HybridWebView 生成 .NET MAUI 应用,需要:

  • 应用的 Web 内容,由静态 HTML、JavaScript、CSS、图像和其他文件组成。
  • 作为应用 UI 的一部分的 HybridWebView 控件。 这可以通过在应用的 XAML 中引用它来实现。
  • Web 内容和 C#/.NET 中的代码,使用 HybridWebView API 在两个组件之间发送消息。

整个应用(包括 Web 内容)已打包并在设备上本地运行,并可以发布到对应的应用商店。 Web 内容托管在本机 Web 视图控件中,并在应用的上下文中运行。 应用的任何部分都可以访问外部 Web 服务,但这不是必须的。

有关详细信息,请参阅 HybridWebView

适用于 Windows 的标题栏

TitleBar 控件提供在 Windows 上向应用添加自定义标题栏的功能:

.NET MAUI 标题栏概述。

可以在任何 Window 上将 TitleBar 设置为 Window.TitleBar 属性的值:

<Window.TitleBar>
    <TitleBar x:Name="TeamsTitleBar"
              Title="Hello World"
              Icon="appicon.png"
              HeightRequest="46">
        <TitleBar.Content>
            <Entry x:Name="SearchTitleBar"
                   Placeholder="Search"
                   VerticalOptions="Center"
                   MinimumWidthRequest="300"
                   MaximumWidthRequest="450"
                   HeightRequest="32"/>
        </TitleBar.Content>
    </TitleBar>
</Window.TitleBar>

其在 C# 中使用的示例为:

Window.TitleBar = new TitleBar
{
    Title = "MAUI App",
    Icon = "appicon.png",
    HeightRequest = 46,
    LeadingContent = new AvatarButton()
};

TitleBar 可通过其 ContentLeadingContentTrailingContent 属性进行高度自定义:

<TitleBar Title="My App"
          BackgroundColor="#512BD4"
          HeightRequest="48">
    <TitleBar.Content>
        <SearchBar Placeholder="Search"
                   MaximumWidthRequest="300"
                   HorizontalOptions="FillAndExpand"
                   VerticalOptions="Center" />
    </TitleBar.Content>
    <TitleBar.TrailingContent>
        <ImageButton HeightRequest="36"
                     WidthRequest="36"
                     BorderWidth="0"
                     Background="Transparent">
            <ImageButton.Source>
                <FontImageSource Size="16"
                                 Glyph="&#xE713;"
                                 FontFamily="SegoeMDL2"/>
            </ImageButton.Source>
        </ImageButton>
    </TitleBar.TrailingContent>
</TitleBar>

以下屏幕截图显示了结果:

.NET MAUI 标题栏屏幕截图。

注意

将在未来版本中添加对 TitleBar 控件的 Mac Catalyst 支持。

控件增强功能

.NET MAUI 9 包括控件增强功能。

BackButtonBehavior OneWay 绑定模式

Shell 应用中 BackButtonBehaviorIsVisibleIsEnabled 的绑定模式现在为 BindingMode.OneWay,而不是 BindingMode.OneTime。 这样就可以更轻松地通过数据绑定在运行时控制后退按钮的行为:

<ContentPage ...>    
    <Shell.BackButtonBehavior>
        <BackButtonBehavior Command="{Binding BackCommand}"
                            IsVisible="{Binding IsBackButtonVisible}"
                            IconOverride="back.png" />   
    </Shell.BackButtonBehavior>
    ...
</ContentPage>

BlazorWebView

在 iOS 和 Mac Catalyst 18 上,.NET MAUI 9 将 BlazorWebView 中托管内容的默认行为更改为 localhost。 用于托管内容的内部 0.0.0.0 地址不再有效,导致 BlazorWebView 无法加载任何内容并呈现为空矩形。

若要选择使用 0.0.0.0 地址,请将以下代码添加到 MauiProgram 类:

// Set this switch to use the LEGACY behavior of always using 0.0.0.0 to host BlazorWebView
AppContext.SetSwitch("BlazorWebView.AppHostAddressAlways0000", true);

如果在 Android 上遇到 BlazorWebView 挂起的情况,则应在 MauiProgram 类的 CreateMauiApp 方法中启用 AppContext 开关:

AppContext.SetSwitch("BlazorWebView.AndroidFireAndForgetAsync", true);

此开关允许 BlazorWebView 触发和忘记发生的异步处置,从而修复 Android 上发生的大多数处置死锁。 有关详细信息,请参阅 修复 Android 上的处置死锁

CollectionView 和 CarouselView

.NET MAUI 9 在 iOS 和 Mac Catalyst 上包含两个可选的新处理程序,可提高 CollectionViewCarouselView 的性能和稳定性。 这些处理程序基于 UICollectionView API。

若要选择使用这些处理程序,请将以下代码添加到 MauiProgram 类:

#if IOS || MACCATALYST
builder.ConfigureMauiHandlers(handlers =>
{
    handlers.AddHandler<Microsoft.Maui.Controls.CollectionView, Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2>();
    handlers.AddHandler<Microsoft.Maui.Controls.CarouselView, Microsoft.Maui.Controls.Handlers.Items2.CarouselViewHandler2>();
});
#endif

标签文本对齐方式

现在,可以使用 HorizontalTextAlignment.Justify 水平对齐 Label 对象中的文本:

<Label Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. In facilisis nulla eu felis fringilla vulputate."
       HorizontalTextAlignment="Justify"/>

软键盘输入支持

.NET MAUI 9 添加了对 PasswordDateTime 的新软键盘输入支持。 可以针对 EditorEntry 控件启用这些功能:

<Entry Keyboard="Date" />

TimePicker

TimePicker 获取一个 TimeSelected 事件,在所选时间更改时会触发该事件。 TimeSelected 事件附带的 TimeChangedEventArgs 对象具有 NewTimeOldTime 属性,分别指定新时间和旧时间。

WebView

WebView 添加了 ProcessTerminated 事件,当 WebView 进程意外结束时,将引发该事件。 此事件附带的 WebViewProcessTerminatedEventArgs 对象定义特定于平台的属性,这些属性表示进程失败的原因。

应用生命周期

.NET MAUI 9 在 iOS 和 Mac Catalyst 上添加了以下远程通知生命周期方法:

  • RegisteredForRemoteNotifications,当应用成功注册远程通知时调用。
  • ReceivedRemoteNotifications,收到远程通知时调用。

以下示例演示如何使用这些生命周期方法:

using Microsoft.Maui.LifecycleEvents;

namespace PlatformLifecycleDemo;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureLifecycleEvents(events =>
            {
#if IOS || MACCATALYST
                events.AddiOS(ios => ios
                    .ReceivedRemoteNotifications((app, dictionary) => LogEvent(nameof(iOSLifecycle.OnReceivedRemoteNotifications)))
                    .RegisteredForRemoteNotifications((app, data) => LogEvent(nameof(iOSLifecycle.OnRegisteredForRemoteNotifications)));
#endif
                static bool LogEvent(string eventName, string type = null)
                {
                    System.Diagnostics.Debug.WriteLine($"Lifecycle event: {eventName}{(type == null ? string.Empty : $" ({type})")}");
                    return true;
                }
            });

        return builder.Build();
    }
}

编译的绑定

默认情况下,除非为应用启用了 NativeAOT,否则 .NET MAUI 不会为不使用已编译绑定的绑定生成警告。 但是,可以在应用程序的项目文件(*.csproj)中将 $(MauiStrictXamlCompilation) 生成属性设置为 true,从而选择生成编译绑定警告:

<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>

处理程序断开连接

使用处理程序实现自定义控件时,每个平台处理程序实现都需要实现 DisconnectHandler 方法,以执行任何本机视图清理,例如取消订阅事件。 但是,在 .NET MAUI 9 之前,.NET MAUI 有意不调用 DisconnectHandler 实现。 相反,在选择清理控件时(例如,在应用中向后导航时),必须自行调用它。

在 .NET MAUI 9 中,处理程序会在可能得情况下自动与其控件断开连接,例如在应用中向后导航时。 在某些情况下,你可能不希望出现此行为。 因此,.NET MAUI 9 添加了 HandlerProperties.DisconnectPolicy 附加属性,用于控制处理程序何时与其控件断开连接。 此属性需要 HandlerDisconnectPolicy 参数,其中 HandlerDisconnectPolicy 枚举定义以下值:

  • Automatic,指示处理程序将自动断开连接。 这是附加属性 HandlerProperties.DisconnectPolicy 的默认值。
  • Manual,表示处理程序必须通过调用 DisconnectHandler 实现来手动断开连接。

以下示例演示如何设置 HandlerProperties.DisconnectPolicy 附加属性:

<controls:Video x:Name="video"
                HandlerProperties.DisconnectPolicy="Manual"
                Source="video.mp4"
                AutoPlay="False" />

等效 C# 代码如下:

Video video = new Video
{
    Source = "video.mp4",
    AutoPlay = false
};
HandlerProperties.SetDisconnectPolicy(video, HandlerDisconnectPolicy.Manual);

此外,还有一种 DisconnectHandlers 扩展方法可将处理程序与给定 IView 断开连接:

video.DisconnectHandlers();

断开连接时,DisconnectHandlers 方法将沿控件树向下传播,直到完成或到达已设置手动策略的控件。

多窗口支持

.NET MAUI 9 添加了在 Mac Catalyst 和 Windows 上通过 Application.Current.ActivateWindow 方法将特定窗口置于前面的功能:

Application.Current?.ActivateWindow(windowToActivate);

本机嵌入

.NET MAUI 9 包括用于本机嵌入方案的完整 API,以前必须手动将其添加到项目中:

var mauiApp = MauiProgram.CreateMauiApp();

#if ANDROID
var mauiContext = new MauiContext(mauiApp.Services, window);
#else
var mauiContext = new MauiContext(mauiApp.Services);
#endif

var mauiView = new MyMauiContent();
var nativeView = mauiView.ToPlatform(mauiContext);

或者,可以使用 ToPlatformEmbedded 方法,传入应用所运行平台的 Window

var mauiApp = MauiProgram.CreateMauiApp();
var mauiView = new MyMauiContent();
var nativeView = mauiView.ToPlatformEmbedded(mauiApp, window);

在这两个示例中,nativeView 都是 mauiView 特定于平台的版本。

若要在 .NET MAUI 9 中启动本机嵌入式应用,请在 MauiAppBuilder 对象上调用 UseMauiEmbeddedApp 扩展方法:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();

        builder
            .UseMauiEmbeddedApp<App>();

        return builder.Build();
    }
}

项目模板

.NET MAUI 9 向 Visual Studio 添加了 .NET MAUI Blazor Hybrid 和 Web 应用 项目模板,该模板使用 .NET MAUI Blazor Hybrid 应用和 Blazor Web 应用创建解决方案,其能够在 Razor 类库项目中共享通用代码。

此模板也可用于 dotnew new

dotnet new maui-blazor-web -n AllTheTargets

资源字典

在 .NET MAUI 9 中,独立 XAML ResourceDictionary(不受代码隐藏文件支持)默认已编译 XAML。 要选择退出此行为,请在 XML 标头后指定 <?xaml-comp compile="false" ?>

剪裁功能开关

.NET MAUI 的若干区域附带了剪裁器指令(称为功能开关),因此可以在 TrimMode=full 时删除禁用功能的代码以及 NativeAOT 的代码:

MSBuild 属性 说明
MauiEnableVisualAssemblyScanning 设置为 true 时,.NET MAUI 将扫描程序集以获取实现 IVisual 的类型和 [assembly:Visual(...)] 属性,并将注册这些类型。 默认情况下,此生成属性设置为 false
MauiShellSearchResultsRendererDisplayMemberNameSupported 设置为 false 时,将忽略 SearchHandler.DisplayMemberName 的值。 相反,应提供 ItemTemplate 来定义 SearchHandler 结果的外观。 默认情况下,此生成属性设置为 true
MauiQueryPropertyAttributeSupport 当设置为 false 时,导航时将不会使用 [QueryProperty(...)] 属性来设置属性值。 相反,应实现 IQueryAttributable 接口以接受查询参数。 默认情况下,此生成属性设置为 true
MauiImplicitCastOperatorsUsageViaReflectionSupport 设置为 false 时,在将值从一种类型转换为另一种类型时,.NET MAUI 不会查找隐式强制转换运算符。 这可能会影响具有不同类型的属性之间的绑定,以及设置具有不同类型值的可绑定对象的属性值。 相反,应为类型定义 TypeConverter,并使用 [TypeConverter(typeof(MyTypeConverter))] 特性将其附加到该类型。 默认情况下,此生成属性设置为 true
_MauiBindingInterceptorsSupport 设置为 false 时,.NET MAUI 不会截获对 SetBinding 方法的任何调用,也不会尝试编译它们。 默认情况下,此生成属性设置为 true

若要使用功能开关,应将相应的 MSBuild 属性放入应用的项目文件(*.csproj),这会导致从 .NET MAUI 程序集中剪裁相关代码。 当与 Full 剪裁模式结合使用时,禁用应用不需要的功能有助于缩小应用的大小。

XAML

实现 IMarkupExtensionIMarkupExtension<T>IValueProviderIExtendedTypeConverter 的所有类都需要通过 RequireServiceAttributeAcceptEmptyServiceProviderAttribute 进行批注。 之所以需要这样做,是因为 .NET MAUI 9 中引入的 XAML 编译器优化可生成更高效的代码,这有助于缩小应用大小并提高运行时性能。

有关使用这些属性批注标记扩展的信息,请参阅服务提供商

Xcode 同步

.NET MAUI 9 包括 Xcode 同步 (xcsync),这是一种工具,让你能够使用 Xcode 通过 .NET 项目管理 Apple 特定文件,包括资产目录、plist 文件、情节提要和 xib 文件。 该工具有两个主要命令,一是根据 .NET 项目生成临时 Xcode 项目,二是将 Xcode 文件中的更改同步回 .NET 项目。

可以将 dotnet buildxcsync-generatexcsync-sync 命令一起使用,以生成或同步这些文件,并传入项目文件和其他参数:

dotnet build /t:xcsync-generate
    /p:xcSyncProjectFile=<PROJECT>
    /p:xcSyncXcodeFolder=<TARGET_XCODE_DIRECTORY>
    /p:xcSyncTargetFrameworkMoniker=<FRAMEWORK>
    /p:xcSyncVerbosity=<LEVEL>

有关详细信息,请参阅 Xcode 同步

弃用的 API

.NET MAUI 9 弃用了某些 API,这些 API 将在未来版本中完全删除。

Frame

Frame 控件在 .NET MAUI 9 中标记为已过时,并将在未来版本中完全删除。 应使用 Border 控件代替它。 有关详细信息,请参阅“边框”。

MainPage

应在 Window 上将 Page 属性设置为应用的首页,而不是在 Application 对象上使用 MainPage 属性定义应用的首页。 这是设置 MainPage 属性时 .NET MAUI 内部发生的情况,因此 MainPage 属性未引入任何标记为已过时的行为更改。

以下示例演示如何通过 CreateWindow 重写在 Window 上设置 Page 属性:

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }

    protected override Window CreateWindow(IActivationState? activationState)
    {
        return new Window(new AppShell());
    }
}

.NET MAUI 9 保留了 MainPage 属性,但将在未来版本中完全删除。

兼容性布局

Microsoft.Maui.Controls.Compatibility 命名空间中的兼容性布局类已过时。

旧度量值调用

以下 VisualElement 旧度量方法已过时:

  • protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
  • public virtual SizeRequest Measure(double widthConstraint, double heightConstraint, MeasureFlags flags = MeasureFlags.None) 来自 VisualElement

作为替代方法,引入了以下方法:

  • public size Measure(double widthConstraint, double heightConstraint)

Measure 方法返回元素在设备上显示所需的最小尺寸。 边距不包括在度量中,但会与尺寸一起返回。 这是测量视图时要调用的首选方法。

此外,Microsoft.Maui.SizeRequest 结构已过时。 相反,应使用 Microsoft.Maui.Size

.NET for Android

.NET for Android 9 添加了对 API 35 的支持,包括缩短生成时间和提高应用的可剪裁性,从而减小尺寸并提高性能。 有关 .NET for Android 9 的详细信息,请参阅以下发行说明:

资产包

.NET for Android 9 引入了将资产放入单独包(称为资产包)的功能。 此功能支持上传通常大于 Google Play 允许的基本包大小的游戏和应用。 通过将这些资产放入单独的包中,可以上传大小高达 2Gb 的包,而非只能上传 200Mb 大小的基本包。

重要

资产包只能包含资产。 对于 .NET for Android,这意味着具有 AndroidAsset 生成操作的项目。

.NET MAUI 应用通过 MauiAsset 生成操作定义资产。 可以通过 AssetPack 属性指定资产包:

<MauiAsset
    Include="Resources\Raw\**"
    LogicalName="%(RecursiveDir)%(Filename)%(Extension)"
    AssetPack="myassetpack" />

注意

其他平台将忽略其他元数据。

如果要将特定项放置在资产包中,则可以使用 Update 属性来定义 AssetPack 元数据:

<MauiAsset Update="Resources\Raw\MyLargeAsset.txt" AssetPack="myassetpack" />

资产包可以有不同的传递选项,用于控制何时在设备上安装资产:

  • 安装时包与应用同时安装。 此类型包的大小最多可为 1Gb,但只能有一个此类型的包。 此传递类型使用 InstallTime 元数据指定。
  • 应用完成安装后不久,即会安装快速跟进包。 在安装这种类型的包时,该应用将能够启动,因此应该先检查它是否已完成安装,然后再尝试使用资产。 此类资产包的大小可达 512Mb。 此传递类型使用 FastFollow 元数据指定。
  • 按需包只有在应用专门发出请求时,才会下载到设备。 所有资产包的总大小不能超过 2Gb,最多可以有 50 个单独的资产包。 此传递类型使用 OnDemand 元数据指定。

在 .NET MAUI 应用中,可以使用 MauiAsset 上的 DeliveryType 属性指定传递类型:

<MauiAsset Update="Resources\Raw\myvideo.mp4" AssetPack="myassetpack" DeliveryType="FastFollow" />

有关 Android 资产包的详细信息,请参阅 Android 资产包

Android 15 支持

.NET for Android 9 添加了适用于 Android 15 的 .NET 绑定 (API 35)。 若要为这些 API 生成解决方案,请更新项目的目标框架:

<TargetFramework>net9.0-android35</TargetFramework>

LLVM 封送方法

现在,.NET for Android 9 在非 Blazor 应用中会默认启用低级别虚拟机 (LLVM) 编译方法。 这使得测试应用的性能提高了约 10%

可以在项目文件 (.csproj) 中禁用 LLVM 封送方法

<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0-android'">
    <AndroidEnableLLVM>false</AndroidEnableLLVM>
    <AndroidEnableLLVMOptimizations>false</AndroidEnableLLVMOptimizations>
</PropertyGroup>

剪裁增强功能

.NET for Android 9 包括针对使用完全剪裁来减少应用大小的修补。 完全剪裁通常仅针对应用的发行版本启用,并且可以在项目文件 (.csproj) 中进行配置

<PropertyGroup Condition="'$(Configuration)' == 'Release' And '$(TargetFramework)' == 'net9.0-android'">
    <TrimMode>Full</TrimMode>
</PropertyGroup>

.NET for iOS

iOS、tvOS、Mac Catalyst 和 macOS 上的 .NET 9 将 Xcode 15.4 用于以下平台版本:

  • iOS:17.5
  • tvOS:17.5
  • Mac Catalyst:17.5
  • macOS:14.5

要详细了解 iOS、tvOS、Mac Catalyst 和 macOS 上的 .NET 9,请参阅以下发行说明:

绑定

.NET for iOS 9 引入了对多目标 .NET 版本进行 iOS 绑定的功能。 例如,可能需要为两个不同的 iOS 版本生成某个库项目:

<TargetFrameworks>net9.0-ios17.0;net9.0-ios17.2</TargetFrameworks>

这将生成两个库,一个使用 iOS 17.0 绑定,一个使用 iOS 17.2 绑定。

重要

应用项目应始终面向最新的 iOS SDK。

适用于 iOS 和 Mac Catalyst 的本机 AOT

在 .NET for iOS 9 中,适用于 iOS 和 Mac Catalyst 的本机提前 (AOT) 编译利用完整剪裁来减少应用的包大小和启动性能。 这是一项发布功能,当你准备好交付应用时可以使用它。

重要

应用及其依赖项必须完全可剪裁才能利用此功能。

另请参阅