maui-blazor-development
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese.NET MAUI Blazor Hybrid Development
.NET MAUI Blazor Hybrid开发
Expert guidance for building cross-platform apps with .NET MAUI and Blazor.
针对使用.NET MAUI和Blazor构建跨平台应用的专家指导。
Implementation Workflow
实现工作流
1. Analysis Phase (Required)
1. 分析阶段(必填)
Before implementation, determine:
- App type: Pure MAUI Blazor or MAUI + Blazor Web App (shared UI)
- Platform targets: Android, iOS, Windows, macOS
- Native features needed: Sensors, camera, file system, notifications
- State management: Component state, MVVM, or hybrid approach
- Navigation pattern: Blazor-only, Shell, or mixed navigation
在实施前,需确定:
- 应用类型:纯MAUI Blazor应用 或 MAUI + Blazor Web应用(共享UI)
- 目标平台:Android、iOS、Windows、macOS
- 所需原生功能:传感器、相机、文件系统、通知
- 状态管理:组件状态、MVVM或混合方式
- 导航模式:仅Blazor导航、Shell导航或混合导航
2. Architecture Decision
架构决策
| Scenario | Recommended Approach |
|---|---|
| Mobile-first with some web | |
| Shared UI across mobile + web | |
| Complex native integration | MVVM with platform services |
| Simple data-driven UI | Component state with DI services |
| 场景 | 推荐方案 |
|---|---|
| 移动端优先,兼顾Web | |
| 移动端与Web共享UI | 搭配RCL的 |
| 复杂原生集成 | 结合平台服务的MVVM模式 |
| 简单数据驱动UI | 依赖DI服务的组件状态管理 |
3. Project Setup
3. 项目设置
csharp
// MauiProgram.cs - Essential setup
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 services
builder.Services.AddSingleton<IDeviceService, DeviceService>();
builder.Services.AddScoped<IDataService, DataService>();
return builder.Build();
}csharp
// MauiProgram.cs - Essential setup
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 services
builder.Services.AddSingleton<IDeviceService, DeviceService>();
builder.Services.AddScoped<IDataService, DataService>();
return builder.Build();
}4. BlazorWebView Configuration
4. BlazorWebView配置
xml
<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui">
<b:BlazorWebView HostPage="wwwroot/index.html">
<b:BlazorWebView.RootComponents>
<b:RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
</b:BlazorWebView.RootComponents>
</b:BlazorWebView>
</ContentPage>xml
<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui">
<b:BlazorWebView HostPage="wwwroot/index.html">
<b:BlazorWebView.RootComponents>
<b:RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
</b:BlazorWebView.RootComponents>
</b:BlazorWebView>
</ContentPage>Core Patterns
核心模式
Dependency Injection
依赖注入
| Lifetime | Use Case |
|---|---|
| App-wide state, device services, settings |
| Per-BlazorWebView instance state |
| Stateless utilities, factories |
csharp
// Interface for platform-specific implementation
public interface IDeviceService
{
string GetDeviceModel();
Task<bool> HasPermissionAsync(string permission);
}
// Platform implementation registered in MauiProgram.cs
builder.Services.AddSingleton<IDeviceService, DeviceService>();| 生命周期 | 使用场景 |
|---|---|
| 应用全局状态、设备服务、设置 |
| 每个BlazorWebView实例的状态 |
| 无状态工具类、工厂类 |
csharp
// Interface for platform-specific implementation
public interface IDeviceService
{
string GetDeviceModel();
Task<bool> HasPermissionAsync(string permission);
}
// Platform implementation registered in MauiProgram.cs
builder.Services.AddSingleton<IDeviceService, DeviceService>();Component Lifecycle
组件生命周期
csharp
@code {
[Parameter] public string Id { get; set; } = "";
// Called once when component initializes
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
}
// Called when parameters change (including first render)
protected override void OnParametersSet()
{
// React to parameter changes
}
// Called after each render
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// JS interop safe here
}
}
}csharp
@code {
[Parameter] public string Id { get; set; } = "";
// Called once when component initializes
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
}
// Called when parameters change (including first render)
protected override void OnParametersSet()
{
// React to parameter changes
}
// Called after each render
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// JS interop safe here
}
}
}Platform Feature Access
平台功能访问
csharp
// Check platform and access native features
@inject IDeviceService DeviceService
@if (DeviceInfo.Current.Platform == DevicePlatform.Android)
{
<AndroidSpecificComponent />
}
@code {
private async Task AccessCameraAsync()
{
var status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
}
if (status == PermissionStatus.Granted)
{
// Use camera
}
}
}csharp
// Check platform and access native features
@inject IDeviceService DeviceService
@if (DeviceInfo.Current.Platform == DevicePlatform.Android)
{
<AndroidSpecificComponent />
}
@code {
private async Task AccessCameraAsync()
{
var status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
}
if (status == PermissionStatus.Granted)
{
// Use camera
}
}
}State Updates from External Events
外部事件触发的状态更新
csharp
@implements IDisposable
@inject IDataService DataService
<p>@message</p>
@code {
private string message = "";
protected override void OnInitialized()
{
DataService.OnDataChanged += HandleDataChanged;
}
private async void HandleDataChanged(object? sender, EventArgs e)
{
message = "Data updated!";
await InvokeAsync(StateHasChanged); // Required for external events
}
public void Dispose()
{
DataService.OnDataChanged -= HandleDataChanged;
}
}csharp
@implements IDisposable
@inject IDataService DataService
<p>@message</p>
@code {
private string message = "";
protected override void OnInitialized()
{
DataService.OnDataChanged += HandleDataChanged;
}
private async void HandleDataChanged(object? sender, EventArgs e)
{
message = "Data updated!";
await InvokeAsync(StateHasChanged); // Required for external events
}
public void Dispose()
{
DataService.OnDataChanged -= HandleDataChanged;
}
}Reference Documentation
参考文档
For detailed patterns and examples, see:
- Project Structure: Solution templates, RCL setup, multi-targeting
- Blazor Components: Lifecycle, RenderFragment, EventCallback, data binding
- Platform Integration: Device APIs, permissions, platform-specific code
- Navigation & Routing: Blazor routing, Shell, deep linking
- State Management: MVVM, DI patterns, CommunityToolkit.Mvvm
如需详细的模式和示例,请参阅:
- 项目结构:解决方案模板、RCL设置、多目标平台配置
- Blazor组件:生命周期、RenderFragment、EventCallback、数据绑定
- 平台集成:设备API、权限、平台特定代码
- 导航与路由:Blazor路由、Shell、深度链接
- 状态管理:MVVM、DI模式、CommunityToolkit.Mvvm
Quick Reference
快速参考
Common Service Registration
常见服务注册
csharp
// Services
builder.Services.AddSingleton<ISettingsService, SettingsService>();
builder.Services.AddSingleton<IConnectivity>(Connectivity.Current);
builder.Services.AddSingleton<IGeolocation>(Geolocation.Default);
// ViewModels (if using MVVM)
builder.Services.AddTransient<MainViewModel>();
builder.Services.AddTransient<SettingsViewModel>();
// Pages with DI
builder.Services.AddTransient<MainPage>();csharp
// Services
builder.Services.AddSingleton<ISettingsService, SettingsService>();
builder.Services.AddSingleton<IConnectivity>(Connectivity.Current);
builder.Services.AddSingleton<IGeolocation>(Geolocation.Default);
// ViewModels (if using MVVM)
builder.Services.AddTransient<MainViewModel>();
builder.Services.AddTransient<SettingsViewModel>();
// Pages with DI
builder.Services.AddTransient<MainPage>();Platform Checks
平台检查
csharp
// Runtime platform check
if (DeviceInfo.Current.Platform == DevicePlatform.iOS) { }
if (DeviceInfo.Current.Platform == DevicePlatform.Android) { }
if (DeviceInfo.Current.Platform == DevicePlatform.WinUI) { }
if (DeviceInfo.Current.Platform == DevicePlatform.macOS) { }
// Compile-time platform check
#if ANDROID
// Android-specific code
#elif IOS
// iOS-specific code
#elif WINDOWS
// Windows-specific code
#endifcsharp
// Runtime platform check
if (DeviceInfo.Current.Platform == DevicePlatform.iOS) { }
if (DeviceInfo.Current.Platform == DevicePlatform.Android) { }
if (DeviceInfo.Current.Platform == DevicePlatform.WinUI) { }
if (DeviceInfo.Current.Platform == DevicePlatform.macOS) { }
// Compile-time platform check
#if ANDROID
// Android-specific code
#elif IOS
// iOS-specific code
#elif WINDOWS
// Windows-specific code
#endifNavigation Patterns
导航模式
csharp
// Blazor navigation (within BlazorWebView)
@inject NavigationManager Navigation
Navigation.NavigateTo("/details/123");
// MAUI navigation (to other pages)
await Navigation.PushAsync(new SettingsPage());
await Shell.Current.GoToAsync("//settings");csharp
// Blazor navigation (within BlazorWebView)
@inject NavigationManager Navigation
Navigation.NavigateTo("/details/123");
// MAUI navigation (to other pages)
await Navigation.PushAsync(new SettingsPage());
await Shell.Current.GoToAsync("//settings");