使用 BlazorWebView 在 .NET MAUI 应用中托管 Blazor Web 应用
.NET 多平台应用 UI (.NET MAUI) BlazorWebView 是一个控件,可用于在 .NET MAUI 应用中托管 Blazor Web 应用。 这些应用称为 Blazor 混合应用,使 Blazor Web 应用能够与平台功能和 UI 控件集成。 BlazorWebView 控件可以添加到 .NET MAUI 应用的任何页面,并指向 Blazor 应用的根目录。 Razor 组件在 .NET 进程中本机运行,并将 Web UI 呈现到嵌入式 Web 视图控件。 在 .NET MAUI 中,Blazor 混合应用可以在 .NET MAUI 支持的所有平台上运行。
BlazorWebView 定义以下属性:
- HostPage,类型为
string?
,用于定义 Blazor Web 应用的根目录页。 - RootComponents,类型为
RootComponentsCollection
,指定可添加到控件的根组件的集合。 - StartPath,类型为
string
,用于定义 Blazor 导航上下文中完成加载 Blazor 组件时初始导航的路径。
RootComponent 类定义以下属性:
- Selector,类型为
string?
,用于定义 CSS 选择器字符串,该字符串指定应在文档中放置组件的位置。 - ComponentType,类型为
Type?
,用于定义根组件的类型。 - Parameters,类型为
IDictionary<string, object?>?
,用于表示要传递给根组件的可选参数字典。
此外,BlazorWebView 定义以下事件:
- BlazorWebViewInitializing,包含一个随附的
BlazorWebViewInitializingEventArgs
对象,该对象在 BlazorWebView 初始化之前引发。 此事件允许自定义 BlazorWebView 配置。 - BlazorWebViewInitialized,附带
BlazorWebViewInitializedEventArgs
对象,该对象在 BlazorWebView 初始化后,呈现任何组件之前引发。 此事件允许检索特定于平台的 Web 视图实例。 - UrlLoading,随附
UrlLoadingEventArgs
对象,单击 BlazorWebView 内某个超链接时引发。 此事件允许自定义超链接是否在 BlazorWebView 中,在外部应用中打开,或者是否 URL 加载尝试已取消。
现有 Razor 组件可以通过将代码移动到应用中,或通过引用包含组件的现有类库或包,在 .NET MAUI Blazor 应用中使用。 详情请参阅在 ASP.NET Core Blazor Hybrid 中重复使用 Razor 组件。
浏览器开发者工具可用于检查 .NET MAUI Blazor 应用。 有关详细信息,请参阅 将浏览器开发者工具与“ASP.NET Core Blazor 混合”配合使用。
注意
虽然 Visual Studio 安装开发 .NET MAUI Blazor 应用所需的所有工具,但 Windows 上的 .NET MAUI Blazor 应用的最终用户必须安装 WebView2 运行时。
有关 Blazor 混合应用的详细信息,请参阅 ASP.NET Core Blazor 混合。
创建 .NET MAUI Blazor 应用
.NET MAUI Blazor 应用可以通过 .NET MAUI Blazor 应用模板在Visual Studio中创建:
这个项目模板创建了一个多目标 .NET MAUI Blazor 应用,可以部署到 Android、iOS、macOS 和 Windows 上。 有关创建 .NET MAUI Blazor 应用的分步说明,请参阅生成 .NET MAUI Blazor 应用。
项目模板创建的 BlazorWebView 是在 MainPage.xaml 中定义的,并指向 Blazor 应用的根目录:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BlazorWebViewDemo"
x:Class="BlazorWebViewDemo.MainPage"
BackgroundColor="{DynamicResource PageBackgroundColor}">
<BlazorWebView HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
</ContentPage>
应用的根 Razor 组件 位于 Main.razor 中,Razor 编译为应用程序根命名空间中命名为 Main
的类型。 其余 Razor 组件位于“Pages”和“共享”项目文件夹中,与默认 Blazor Web 模板中使用的组件相同。 应用的静态 Web 资产位于 wwwroot 文件夹中。
将 BlazorWebView 添加到现有应用
向现有 .NET MAUI 应用添加 BlazorWebView 的过程如下所示:
通过编辑其 CSPROJ 项目文件的第一行,将 Razor SDK,
Microsoft.NET.Sdk.Razor
添加到项目:<Project Sdk="Microsoft.NET.Sdk.Razor">
为 Blazor 项目生成和打包包含 Razor 文件的项目需要 Razor SDK。
将应用的根 Razor组件添加到项目中。
将 Razor 组件添加到名为“Pages”和“共享”的项目文件夹中。
将静态 Web 资产添加到名为 wwwroot 的项目文件夹中。
将任何可选的 _Imports.razor 文件添加到项目。
将 BlazorWebView 添加到 .NET MAUI 应用中的页面,并将其指向 Blazor 应用的根目录:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:MyBlazorApp" x:Class="MyBlazorApp.MainPage"> <BlazorWebView HostPage="wwwroot/index.html"> <BlazorWebView.RootComponents> <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" /> </BlazorWebView.RootComponents> </BlazorWebView> </ContentPage>
修改
MauiProgram
类的CreateMauiApp
方法,以注册 BlazorWebView 控件以在应用中使用。 为此,请在IServiceCollection
对象上调用AddMauiBlazorWebView
方法,将组件 Web 视图服务添加到服务集合:public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }); builder.Services.AddMauiBlazorWebView(); #if DEBUG builder.Services.AddBlazorWebViewDeveloperTools(); #endif // Register any app services on the IServiceCollection object // e.g. builder.Services.AddSingleton<WeatherForecastService>(); return builder.Build(); } }
通过本机 UI 访问范围内的服务
BlazorWebView 具有一种 TryDispatchAsync 方法,该方法以异步方式调用指定的 Action<ServiceProvider>
,并传入 Razor 组件中可用的作用域服务。 这样,本机 UI 中的代码就可以访问范围内服务,例如 NavigationManager:
private async void OnMyMauiButtonClicked(object sender, EventArgs e)
{
var wasDispatchCalled = await blazorWebView.TryDispatchAsync(sp =>
{
var navMan = sp.GetRequiredService<NavigationManager>();
navMan.CallSomeNavigationApi(...);
});
if (!wasDispatchCalled)
{
// Consider what to do if it the dispatch fails - that's up to your app to decide.
}
}
诊断问题
BlazorWebView 具有内置日志记录,可帮助诊断 Blazor Hybrid 应用中的问题。 启用此日志记录分为两个步骤:
- 启用 BlazorWebView 和相关组件来记录诊断信息。
- 配置记录器以将日志输出写入到可以查看的位置。
有关日志记录的详细信息,请参阅 C# 和 .NET 中的日志记录。
启用 BlazorWebView 日志记录
所有日志记录配置都可以作为依赖项注入系统中服务注册的一部分执行。 要在 Microsoft.AspNetCore.Components.WebView 命名空间下为 BlazorWebView 和相关组件启用最大日志记录,请在应用服务的注册位置添加以下代码:
services.AddLogging(logging =>
{
logging.AddFilter("Microsoft.AspNetCore.Components.WebView", LogLevel.Trace);
});
或者,使用以下代码为使用 Microsoft.Extensions.Logging 的每个组件启用最大日志记录:
services.AddLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Trace);
});
配置日志记录输出并查看输出
配置组件以写入日志信息后,需要配置记录器应写入日志的位置,然后查看日志输出。
“调试”日志记录提供程序使用 Debug
语句写入输出,可以从 Visual Studio 查看输出。
要配置“调试”日志记录提供程序,请在项目中添加对 Microsoft.Extensions.Logging.Debug
NuGet 包的引用。 然后,调用 AddDebug 扩展方法,将调用内的提供程序注册至在上一步中添加的 AddLogging:
services.AddLogging(logging =>
{
logging.AddFilter("Microsoft.AspNetCore.Components.WebView", LogLevel.Trace);
logging.AddDebug();
});
从已启用调试的 Visual Studio 运行应用时,可以在 Visual Studio 的“输出”窗口中查看调试输出。
在 iOS 上播放内联视频
若要在 iOS 上的 Blazor 混合应用中播放内联视频,应在 BlazorWebView 中:
将 UrlLoadingStrategy 属性设置为
OpenInWebView
。 可以在 UrlLoading 事件的事件处理程序中完成此操作:private void BlazorUrlLoading(object? sender, UrlLoadingEventArgs e) { #if IOS e.UrlLoadingStrategy = UrlLoadingStrategy.OpenInWebView; #endif }
确保
Configuration
对象中的AllowsInlineMediaPlayback
属性设置为true
。 可以在 BlazorWebViewInitializing 事件的事件处理程序中完成此操作:private void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e) { #if IOS e.Configuration.AllowsInlineMediaPlayback = true; #endif }
修复 Android 上的处置死锁
默认情况下,BlazorWebView 执行异步过度同步处置,这意味着它会阻止线程,直到异步处置完成。 但是,如果处置需要在同一线程上运行代码(因为线程在等待时被阻止),这可能会导致死锁。
如果在 Android 上遇到 BlazorWebView 挂起的情况,则应在 MauiProgram.cs 的 CreateMauiApp
方法中启用 AppContext 开关:
AppContext.SetSwitch("BlazorWebView.AndroidFireAndForgetAsync", true);
此开关允许 BlazorWebView 触发和忘记发生的异步处置,从而修复 Android 上发生的大多数处置死锁。
警告
启用此开关意味着可以在释放所有对象之前返回处置,这可能会导致应用中的行为发生更改。 释放的项部分是 Blazor 自己的内部类型,但也包括应用定义的类型,例如应用的 BlazorWebView 部分中使用的作用域服务。
在 iOS 和 Mac Catalyst 上使用旧行为托管内容
在 iOS 和 Mac Catalyst 18 上,.已将 BlazorWebView 中托管内容的默认行为更改为 localhost
。 用于托管内容的内部 0.0.0.1
地址不再有效,导致 BlazorWebView 无法加载任何内容并呈现为空矩形。
若要选择使用 0.0.0.1
地址,请将以下代码添加到 MauiProgram.cs中的 CreateMauiApp
方法:
// Set this switch to use the LEGACY behavior of always using 0.0.0.1 to host BlazorWebView
AppContext.SetSwitch("BlazorWebView.AppHostAddressAlways0000", true);