名称: dotnet-uno-platform 描述: “构建 Uno Platform 跨平台应用。扩展、MVUX、Toolkit 控件、热重载。” 用户可调用: false
dotnet-uno-platform
Uno Platform 核心开发:扩展生态系统(导航、依赖注入、配置、序列化、本地化、日志记录、HTTP、身份验证)、MVUX 响应式模式、Toolkit 控件、主题资源(Material/Cupertino/Fluent)、热重载和单项目结构。涵盖基于 .NET 8.0+ 的 Uno Platform 5.x+。
范围
- Uno Platform 单项目结构,带条件目标框架
- 扩展生态系统(导航、依赖注入、配置、序列化、HTTP、身份验证)
- MVUX 响应式模式
- Toolkit 控件和主题资源(Material/Cupertino/Fluent)
- 热重载
超出范围
- 按目标部署(WASM、iOS、Android、桌面)——见 [技能:dotnet-uno-targets]
- MCP 服务器集成以获取实时文档——见 [技能:dotnet-uno-mcp]
- Uno Platform 测试——见 [技能:dotnet-uno-testing]
- 通用序列化模式——见 [技能:dotnet-serialization]
- WASM 的 AOT/裁剪——见 [技能:dotnet-aot-wasm]
- UI 框架选择决策树——见 [技能:dotnet-ui-chooser]
交叉引用:[技能:dotnet-uno-targets] 用于按目标部署,[技能:dotnet-uno-mcp] 用于 MCP 集成,[技能:dotnet-uno-testing] 用于测试模式,[技能:dotnet-serialization] 用于序列化深度,[技能:dotnet-aot-wasm] 用于 WASM AOT,[技能:dotnet-ui-chooser] 用于框架选择,[技能:dotnet-accessibility] 用于无障碍模式(WASM 上的 AutomationProperties、ARIA 映射)。
单项目结构
Uno Platform 5.x 使用单项目结构,带条件目标框架以支持多目标。一个 .csproj 通过多目标针对所有平台。
<!-- MyApp.csproj -->
<Project Sdk="Uno.Sdk">
<PropertyGroup>
<TargetFrameworks>
net8.0-browserwasm;
net8.0-ios;
net8.0-android;
net8.0-maccatalyst;
net8.0-windows10.0.19041;
net8.0-desktop
</TargetFrameworks>
<OutputType>Exe</OutputType>
<UnoFeatures>
Extensions;
Toolkit;
Material;
MVUX;
Navigation;
Configuration;
Hosting;
Http;
Localization;
Logging;
LoggingSerilog;
Serialization;
Authentication;
AuthenticationOidc
</UnoFeatures>
</PropertyGroup>
</Project>
UnoFeatures MSBuild 属性控制包含哪些 Uno 扩展和主题包。Uno SDK 自动解析这些功能到正确的 NuGet 包。
项目布局
MyApp/
MyApp/
App.xaml / App.xaml.cs # 应用程序入口、资源字典
MainPage.xaml / .xaml.cs # 初始页面
Presentation/ # ViewModels 或 MVUX Models
Views/ # XAML 页面
Services/ # 服务接口和实现
Strings/ # 本地化资源 (.resw)
en/Resources.resw
Assets/ # 图像、字体、图标
appsettings.json # 配置(Extensions.Configuration)
Platforms/ # 平台特定代码(条件编译)
Android/
iOS/
Wasm/
Desktop/
MyApp.Tests/ # 单元测试(共享逻辑)
Uno 扩展
Uno 扩展在平台之上提供基于观点的架构。所有模块通过主机构建器模式注册。
主机构建器设置
// App.xaml.cs
public App()
{
this.InitializeComponent();
Host = UnoHost
.CreateDefaultBuilder()
.UseConfiguration(configure: configBuilder =>
configBuilder.EmbeddedSource<App>()
.Section<AppConfig>())
.UseLocalization()
.UseNavigation(RegisterRoutes)
.UseSerilog(loggerConfiguration: config =>
config.WriteTo.Debug())
.ConfigureServices((context, services) =>
{
services.AddSingleton<IProductService, ProductService>();
})
.Build();
}
导航
包: Uno.Extensions.Navigation
基于区域的导航,带路由映射、深度链接和类型安全参数传递。导航可从 XAML 声明式或从代码命令式驱动。
// 路由注册
private static void RegisterRoutes(IViewRegistry views, IRouteRegistry routes)
{
views.Register(
new ViewMap(ViewModel: typeof(ShellModel)),
new ViewMap<MainPage, MainModel>(),
new ViewMap<ProductDetailPage, ProductDetailModel>(),
new DataViewMap<ProductDetailPage, ProductDetailModel, ProductEntity>()
);
routes.Register(
new RouteMap("", View: views.FindByViewModel<ShellModel>(),
Nested: new RouteMap[]
{
new("Main", View: views.FindByViewModel<MainModel>()),
new("ProductDetail", View: views.FindByViewModel<ProductDetailModel>())
})
);
}
<!-- XAML 基础导航使用附加属性 -->
<Button Content="查看产品"
uen:Navigation.Request="ProductDetail"
uen:Navigation.Data="{Binding SelectedProduct}" />
关键概念: 基于区域的导航将导航行为附加到可视区域(Frame、NavigationView、TabBar)。路由映射定义导航图。深度链接将 URL 映射到 WASM 的路由。
依赖注入
包: Uno.Extensions.Hosting
使用 Microsoft.Extensions.Hosting 底层。主机构建器模式带服务注册、键控服务和作用域生命周期。
.ConfigureServices((context, services) =>
{
// 标准 DI 注册
services.AddSingleton<IAuthService, AuthService>();
services.AddTransient<IOrderService, OrderService>();
// 键控服务 (.NET 8+)
services.AddKeyedSingleton<ICache>("memory", new MemoryCache());
services.AddKeyedSingleton<ICache>("distributed", new RedisCache());
})
配置
包: Uno.Extensions.Configuration
从 appsettings.json(嵌入式资源)、环境特定覆盖和运行时可写选项加载配置。
// appsettings.json
{
"AppConfig": {
"ApiBaseUrl": "https://api.example.com",
"MaxRetries": 3
}
}
// 绑定到强类型选项
.UseConfiguration(configure: configBuilder =>
configBuilder
.EmbeddedSource<App>()
.Section<AppConfig>())
// AppConfig.cs
public record AppConfig
{
public string ApiBaseUrl { get; init; } = "";
public int MaxRetries { get; init; } = 3;
}
序列化
包: Uno.Extensions.Serialization
集成 System.Text.Json 带源生成器以支持 AOT 兼容性。在扩展生态系统中配置 JSON 序列化。
.UseSerialization(configure: serializerBuilder =>
serializerBuilder
.AddJsonTypeInfo(AppJsonContext.Default.ProductDto)
.AddJsonTypeInfo(AppJsonContext.Default.OrderDto))
对于通用序列化模式和 AOT 源生成深度,见 [技能:dotnet-serialization]。
本地化
包: Uno.Extensions.Localization
基于资源的本地化使用 .resw 文件带运行时文化切换。
.UseLocalization()
<!-- Strings/en/Resources.resw -->
<!-- 名称: MainPage_Title.Text, 值: 欢迎 -->
<!-- 名称: MainPage_LoginButton.Content, 值: 登录 -->
<!-- XAML: 使用 x:Uid 进行自动资源绑定 -->
<TextBlock x:Uid="MainPage_Title" />
<Button x:Uid="MainPage_LoginButton" />
运行时文化切换:
// 编程切换文化
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
await localizationService.SetCurrentCultureAsync(new CultureInfo("fr-FR"));
日志记录
包: Uno.Extensions.Logging
集成 Microsoft.Extensions.Logging。Serilog 集成用于平台特定接收器。
.UseSerilog(loggerConfiguration: config =>
config
.MinimumLevel.Information()
.WriteTo.Debug()
.WriteTo.Console())
平台特定接收器:桌面调试输出、WASM 浏览器控制台、Android 平台 Logcat、iOS NSLog。
HTTP
包: Uno.Extensions.Http
HTTP 客户端集成带端点配置。支持 Refit 用于类型化 API 客户端和 Kiota 用于 OpenAPI 生成的客户端。
.UseHttp(configure: (context, services) =>
services
.AddRefitClient<IProductApi>(context,
configure: builder => builder
.ConfigureHttpClient(client =>
client.BaseAddress = new Uri("https://api.example.com"))))
// Refit 接口
public interface IProductApi
{
[Get("/products")]
Task<List<ProductDto>> GetProductsAsync(CancellationToken ct = default);
[Get("/products/{id}")]
Task<ProductDto> GetProductByIdAsync(int id, CancellationToken ct = default);
}
身份验证
包: Uno.Extensions.Authentication
OIDC、自定义身份验证提供程序和令牌管理。集成导航用于登录/注销流。
.UseAuthentication(auth =>
auth.AddOidc(oidc =>
{
oidc.Authority = "https://login.example.com";
oidc.ClientId = "my-app";
oidc.Scope = "openid profile email";
}))
令牌管理是自动的:令牌按平台安全存储(iOS/macOS 上的 Keychain、Android 上的 KeyStore、Windows 上的 Credential Manager、WASM 上的浏览器存储)并透明刷新。
MVUX(模型-视图-更新-扩展)
MVUX 是 Uno 推荐的响应式模式,不同于 MVVM。它使用不可变记录、Feeds 和 States 以声明式建模数据流。源生成器从普通模型类生成可绑定代理。
核心概念
| 概念 | 目的 | MVVM 等价物 |
|---|---|---|
| 模型 | 定义 UI 状态的不可变记录 | ViewModel |
| Feed | 异步数据源(加载/数据/错误状态) | ObservableCollection + 加载标志 |
| State | 可变响应式状态带变更跟踪 | INotifyPropertyChanged 属性 |
| ListFeed | 专门用于集合的 Feed | ObservableCollection |
| Command | 从公共异步方法自动生成 | ICommand |
模型示例
// ProductModel.cs -- MVUX 模型(源生成器生成可绑定代理)
public partial record ProductModel(IProductService ProductService)
{
// Feed: 异步数据源带加载/错误/数据状态
public IFeed<IImmutableList<ProductDto>> Products => Feed
.Async(async ct => await ProductService.GetProductsAsync(ct));
// State: 可变响应式值
public IState<string> SearchTerm => State<string>.Value(this, () => "");
// ListFeed 带选择支持
public IListFeed<ProductDto> FilteredProducts => SearchTerm
.SelectAsync(async (term, ct) =>
await ProductService.SearchProductsAsync(term, ct))
.AsListFeed();
// Command: 从异步方法签名自动生成
public async ValueTask AddProduct(CancellationToken ct)
{
var term = await SearchTerm;
await ProductService.AddProductAsync(term, ct);
}
}
<!-- ProductPage.xaml -- 绑定到生成的代理 -->
<Page x:Class="MyApp.Views.ProductPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Text="{Binding SearchTerm, Mode=TwoWay}" />
<FeedView Source="{Binding FilteredProducts}">
<FeedView.ValueTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding Data}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</FeedView.ValueTemplate>
<FeedView.ProgressTemplate>
<DataTemplate>
<ProgressRing IsActive="True" />
</DataTemplate>
</FeedView.ProgressTemplate>
<FeedView.ErrorTemplate>
<DataTemplate>
<TextBlock Text="加载产品错误" Foreground="Red" />
</DataTemplate>
</FeedView.ErrorTemplate>
</FeedView>
<Button Content="添加产品" Command="{Binding AddProduct}" />
</StackPanel>
</Page>
MVUX 对比 MVVM
| 关注点 | MVUX | MVVM |
|---|---|---|
| 模型定义 | 不可变 record 类型 |
可变类带 INotifyPropertyChanged |
| 数据加载 | IFeed<T> 带内置加载/错误状态 |
手动加载标志和 try/catch |
| 集合 | IListFeed<T> 带不可变快照 |
ObservableCollection<T> 带突变 |
| 命令 | 从 async 方法自动生成 |
ICommand 实现(RelayCommand) |
| 状态变更 | IState<T> 带显式更新语义 |
属性设置器触发 PropertyChanged |
| 样板代码 | 最小(源生成器) | 显著(基类、属性) |
何时使用 MVUX: 新的 Uno Platform 项目,特别是那些有异步数据源和复杂加载状态的项目。MVUX 消除了大多数样板代码并声明式处理加载/错误状态。
何时使用 MVVM: 从现有 WPF/UWP/WinUI 代码库迁移的项目,熟悉 MVVM 模式的团队,或使用 CommunityToolkit.Mvvm 的项目。
Uno Toolkit 控件
Uno Toolkit 提供跨平台控件和助手,超出标准 WinUI 控件。通过 UnoFeatures 带 Toolkit 启用。
关键控件
| 控件 | 目的 |
|---|---|
AutoLayout |
Flexbox 式布局带间距、填充和对齐 |
Card / CardContentControl |
Material 风格卡片表面带高程 |
Chip / ChipGroup |
过滤器芯片、操作芯片、选择芯片 |
Divider |
水平/垂直分隔线 |
DrawerControl |
侧边抽屉(汉堡菜单) |
LoadingView |
加载状态包装器带骨架/闪烁效果 |
NavigationBar |
跨平台导航栏 |
ResponsiveView |
基于屏幕宽度断点的自适应布局 |
SafeArea |
切口、状态栏、导航栏的插入 |
ShadowContainer |
跨平台下拉阴影通过 ThemeShadow |
TabBar |
底部或顶部标签导航 |
ZoomContentControl |
捏合缩放容器 |
Toolkit 助手
| 助手 | 目的 |
|---|---|
CommandExtensions |
附加命令到任何控件(不只是 Button) |
ItemsRepeaterExtensions |
ItemsRepeater 的选择和命令支持 |
InputExtensions |
自动对焦、返回键命令、输入范围 |
ResponsiveMarkupExtensions |
XAML 标记中的响应式值(例如,Responsive.Narrow) |
StatusBarExtensions |
按平台控制状态栏外观 |
AncestorBinding |
在模板中绑定到祖先 DataContext |
AutoLayout 示例
<!-- 垂直堆栈带间距、填充和对齐 -->
<utu:AutoLayout Spacing="16" Padding="24"
PrimaryAxisAlignment="Start"
CounterAxisAlignment="Stretch">
<TextBlock Text="产品列表"
Style="{StaticResource HeadlineMedium}" />
<utu:AutoLayout Spacing="8" Orientation="Horizontal">
<TextBox PlaceholderText="搜索..."
utu:AutoLayout.PrimaryLength="*" />
<Button Content="搜索"
utu:AutoLayout.CounterAlignment="Center" />
</utu:AutoLayout>
<ListView ItemsSource="{Binding Products}"
utu:AutoLayout.PrimaryLength="*" />
</utu:AutoLayout>
主题资源
Uno 支持 Material、Cupertino 和 Fluent 设计系统作为主题包。主题在所有平台上提供一致的颜色、排版、高程和控件样式。
主题配置
<!-- .csproj 中的 UnoFeatures -->
<UnoFeatures>Material</UnoFeatures> <!-- 或 Cupertino,或两者 -->
<!-- App.xaml -- 主题资源字典 -->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Material 主题资源 -->
<MaterialTheme />
<!-- 可选: 颜色调色板覆盖 -->
<ResourceDictionary Source="ms-appx:///Themes/ColorPaletteOverride.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
颜色自定义
通过 ColorPaletteOverride.xaml 覆盖 Material 主题颜色:
<!-- Themes/ColorPaletteOverride.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="PrimaryColor">#6750A4</Color>
<Color x:Key="SecondaryColor">#625B71</Color>
<Color x:Key="TertiaryColor">#7D5260</Color>
<Color x:Key="ErrorColor">#B3261E</Color>
</ResourceDictionary>
排版
使用主题系统中的现有 TextBlock 样式。从不设置显式字体大小——使用 Material 类型比例:
<TextBlock Text="标题" Style="{StaticResource HeadlineMedium}" />
<TextBlock Text="正文文本" Style="{StaticResource BodyLarge}" />
<TextBlock Text="说明文字" Style="{StaticResource LabelSmall}" />
| 样式 | 典型用途 |
|---|---|
DisplayLarge/Medium/Small |
英雄文本、启动屏幕 |
HeadlineLarge/Medium/Small |
页面标题、部分标题 |
TitleLarge/Medium/Small |
卡片标题、对话框标题 |
BodyLarge/Medium/Small |
段落文本、描述 |
LabelLarge/Medium/Small |
按钮标签、说明文字、元数据 |
ThemeService
编程切换浅色和深色主题:
var themeService = serviceProvider.GetRequiredService<IThemeService>();
await themeService.SetThemeAsync(AppTheme.Dark);
var currentTheme = themeService.Theme;
热重载
Uno Platform 通过其自定义实现在所有目标上提供热重载。XAML 和 C# 代码隐藏的更改无需重启应用程序即可反映。
支持的更改
| 更改类型 | 热重载支持 |
|---|---|
| XAML 布局/样式 | 完全重载,即时 |
| C# 代码隐藏(方法体) | 通过 MetadataUpdateHandler 支持 |
| 新属性/方法 | 需要重新构建 |
| 资源字典更改 | 完全重载 |
| 导航路由更改 | 需要重新构建 |
启用热重载
# 在 dotnet run 前设置环境变量
export DOTNET_MODIFIABLE_ASSEMBLIES=debug
# 运行带热重载
dotnet run -f net8.0-desktop --project MyApp/MyApp.csproj
热重载由 Visual Studio 和 VS Code(带 Uno 扩展)自动配置。对于 CLI 使用,在运行前设置 DOTNET_MODIFIABLE_ASSEMBLIES=debug。
陷阱: 热重载不支持添加新类型、更改继承层次结构或修改 UnoFeatures。这些需要完全重新构建。
代理陷阱
- 不要混淆 MVUX 和 MVVM。 MVUX 使用不可变记录、Feeds 和 States——不是
INotifyPropertyChanged。不要在 MVUX 模型上添加ObservableProperty属性。 - 不要硬编码 Uno 扩展的 NuGet 包版本。
UnoFeaturesMSBuild 属性通过 Uno SDK 自动解析包。为扩展添加显式PackageReference项可能导致版本冲突。 - 不要在 Uno XAML 中使用
{Binding StringFormat=...}。 这是仅限 WPF 的功能。对于格式化文本,使用转换器或多个<Run>元素。 - 不要在绑定中使用
x:Static或{x:Reference}。 这些是仅限 WPF 的标记扩展,在 WinUI/Uno 中不可用。 - 不要设置显式字体大小或粗细。 使用主题的 TextBlock 样式(例如,
HeadlineMedium、BodyLarge)以保持设计系统一致性。 - 不要使用硬编码的十六进制颜色。 始终引用主题资源(
PrimaryColor、SecondaryColor)或语义笔刷以保持主题兼容性。 - 不要在
CommandBar外部使用AppBarButton。 对于独立图标按钮,使用带图标内容的常规Button。 - 不要忘记本地化的
x:Uid。 每个用户可见字符串应使用x:Uid引用.resw资源,而不是硬编码文本。
先决条件
- .NET 8.0+(Uno Platform 5.x 基线)
- Uno SDK(
Uno.Sdk项目 SDK) - 根据需要的工作负载:
dotnet workload install ios android maccatalyst wasm-tools - Visual Studio 2022+ 或带 Uno Platform 扩展的 VS Code