.NET MAUI for .NET 9 中的新增功能
.NET 9 中的 .NET Multi-platform App UI (.NET MAUI) 侧重于提升产品质量。 这包括扩展测试覆盖范围、端到端方案测试和 bug 修复。 若要详细了解 .NET MAUI 9 中的产品质量提升情况,请参阅以下发行说明:
- .NET MAUI 9 预览版 7
- .NET MAUI 9 预览版 6
- .NET MAUI 9 预览版 5
- .NET MAUI 9 预览版 4
- .NET MAUI 9 预览版 3
- .NET MAUI 9 预览版 2
- .NET MAUI 9 预览版 1
重要
由于使用外部依赖项(例如 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 服务,但这不是必须的。
要生成混合应用:
打开现有的 .NET MAUI 应用项目或新建 .NET MAUI 应用项目。
将 Web 内容添加到 .NET MAUI 应用项目。
应用的 Web 内容应作为原始资产包含在 .NET MAUI 项目中。 原始资产是应用的 Resources\Raw 文件夹中的任何文件,包括子文件夹。 对于 HybridWebView,Web 内容应放置在 Resources\Raw\wwwroot 文件夹中,其主文件名为 index.html。
简单的应用可能包含以下文件和内容:
Resources\Raw\wwwroot\index.html,其中包含主 UI 内容:
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title></title> <link rel="icon" href="data:,"> <script src="scripts/HybridWebView.js"></script> <script> window.addEventListener( "HybridWebViewMessageReceived", function (e) { var messageFromCSharp = document.getElementById("messageFromCSharp"); messageFromCSharp.value += '\r\n' + e.detail.message; }); </script> </head> <body> <h1>HybridWebView app!</h1> <div> <button onclick="window.HybridWebView.SendRawMessage('Message from JS!')">Send message to C#</button> </div> <div> Messages from C#: <textarea readonly id="messageFromCSharp" style="width: 80%; height: 300px;"></textarea> </div> </body> </html>
Resources\Raw\wwwroot\scripts\HybridWebView.js,其中包含标准 HybridWebView JavaScript 库:
function HybridWebViewInit() { function DispatchHybridWebViewMessage(message) { const event = new CustomEvent("HybridWebViewMessageReceived", { detail: { message: message } }); window.dispatchEvent(event); } if (window.chrome && window.chrome.webview) { // Windows WebView2 window.chrome.webview.addEventListener('message', arg => { DispatchHybridWebViewMessage(arg.data); }); } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { // iOS and MacCatalyst WKWebView window.external = { "receiveMessage": message => { DispatchHybridWebViewMessage(message); } }; } else { // Android WebView window.addEventListener('message', arg => { DispatchHybridWebViewMessage(arg.data); }); } } window.HybridWebView = { "SendRawMessage": function (message) { if (window.chrome && window.chrome.webview) { // Windows WebView2 window.chrome.webview.postMessage(message); } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { // iOS and MacCatalyst WKWebView window.webkit.messageHandlers.webwindowinterop.postMessage(message); } else { // Android WebView hybridWebViewHost.sendRawMessage(message); } } } HybridWebViewInit();
然后,将任何其他 Web 内容添加到项目。
警告
在某些情况下,Visual Studio 可能会向项目的 .csproj 文件添加不正确的条目。 使用原始资产的默认位置时,.csproj 文件中不应有任何这些文件或文件夹的条目。
将 HybridWebView 控件添加到应用:
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*"> <Button Text="Send message to JavaScript" Clicked="OnSendMessageButtonClicked" /> <HybridWebView x:Name="hybridWebView" RawMessageReceived="OnHybridWebViewRawMessageReceived" Grid.Row="1" /> </Grid>
使用 HybridWebView API 在 JavaScript 和 C# 代码之间发送消息:
private void OnSendMessageButtonClicked(object sender, EventArgs e) { hybridWebView.SendRawMessage($"Hello from C#!"); } private async void OnHybridWebViewRawMessageReceived(object sender, HybridWebViewRawMessageReceivedEventArgs e) { await DisplayAlert("Raw Message Received", e.Message, "OK"); }
因为没有执行其他处理,所以上述消息被归类为原始消息。 还可以对消息中的数据进行编码,以执行更高级的消息传递。
有关详细信息,请参阅 HybridWebView。
适用于 Windows 的标题栏
该 TitleBar
控件提供在 Windows 上向应用添加自定义标题栏的功能:
可以在任何 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
可通过其 Content
、LeadingContent
和 TrailingContent
属性进行高度自定义:
<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=""
FontFamily="SegoeMDL2"/>
</ImageButton.Source>
</ImageButton>
</TitleBar.TrailingContent>
</TitleBar>
以下屏幕截图显示了结果:
注意
将在未来版本中添加对 TitleBar
控件的 Mac Catalyst 支持。
控件增强功能
.NET MAUI 9 包括控件增强功能。
BackButtonBehavior OneWay 绑定模式
Shell 应用中 BackButtonBehavior
的 IsVisible
和 IsEnabled
的绑定模式现在为 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);
CollectionView 和 CarouselView
.NET MAUI 9 在 iOS 和 Mac Catalyst 上包含两个可选的新处理程序,可提高 CollectionView
和 CarouselView
的性能和稳定性。 这些处理程序基于 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
软键盘输入支持
.NET MAUI 9 添加了对 Password
、Date
和 Time
的新软键盘输入支持。 可以针对 Editor 和 Entry 控件启用这些功能:
<Entry Keyboard="Date" />
TimePicker
TimePicker 获取一个 TimeSelected 事件,在所选时间更改时会触发该事件。 TimeSelected
事件附带的 TimeChangedEventArgs 对象具有 NewTime
和 OldTime
属性,分别指定新时间和旧时间。
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();
}
}
处理程序断开连接
使用处理程序实现自定义控件时,每个平台处理程序实现都需要实现 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
Xcode 同步
.NET MAUI 9 包括 Xcode 同步 (xcsync
),这是一种工具,让你能够使用 Xcode 通过 .NET 项目管理 Apple 特定文件,包括资产目录、plist 文件、情节提要和 xib 文件。 该工具有两个主要命令,一是根据 .NET 项目生成临时 Xcode 项目,二是将 Xcode 文件中的更改同步回 .NET 项目。
可以将 dotnet build
与 xcsync-generate
或 xcsync-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 预览版 7
- .NET for Android 9 预览版 6
- .NET for Android 9 预览版 5
- .NET for Android 9 预览版 4
- .NET for Android 9 预览版 3
- .NET for Android 9 预览版 2
- .NET for Android 9 预览版 1
资产包
.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.2:
- iOS:17.2
- tvOS:17.2
- Mac Catalyst:17.2
- macOS:14.2
要详细了解 iOS、tvOS、Mac Catalyst 和 macOS 上的 .NET 9,请参阅以下发行说明:
- .NET 9.0.1xx 预览版 7
- .NET 9.0.1xx 预览版 6
- .NET 9.0.1xx 预览版 5
- .NET 9.0.1xx 预览版 4
- .NET 9.0.1xx 预览版 3
- .NET 9.0.1xx 预览版 2
- .NET 9.0.1xx 预览版 1
绑定
.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) 编译利用完整剪裁来减少应用的包大小和启动性能。 这是一项发布功能,当你准备好交付应用时可以使用它。
重要
应用及其依赖项必须完全可剪裁才能利用此功能。