mcpField
D
dotnet-uno-platform

dotnet-uno-platform

github.com/wshaddix开发8671 次安装35 周前更新
代码仓库

关于 dotnet-uno-platform

面向 Uno Platform 5.x 与 .NET 8.0+ 项目的开发技能,涵盖应用结构、扩展生态、MVUX、控件、主题和 Hot Reload。适用于 Uno 应用开发者,重点是通用项目开发,而非针对平台的部署。

主要功能

  1. 01单项目多目标结构
  2. 02导航与服务扩展
  3. 03MVUX 响应式应用模式
  4. 04Toolkit 控件与辅助功能
  5. 05Material、Cupertino 和 Fluent 主题
  6. 06XAML 与 C# Hot Reload

适用场景

  1. 01规划新的 Uno Platform 应用结构
  2. 02添加响应式数据流和导航
  3. 03使用 Toolkit 控件构建自适应界面
  4. 04配置主题并实时迭代 UI

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-uno-platform

安装后在 Claude Code 中输入/dotnet-uno-platform确认已加载。

详细介绍

dotnet-uno-platform

Uno Platform 核心开发:Extensions 生态系统(导航、DI、配置、序列化、本地化、日志记录、HTTP、身份验证)、MVUX 响应式模式、Toolkit 控件、主题资源(Material/Cupertino/Fluent)、热重载和单项目结构。覆盖基于 .NET 8.0+ 的 Uno Platform 5.x+。

**范围边界:**此技能负责 Uno Platform 项目结构、Extensions 生态系统配置、MVUX 模式、Toolkit 控件、主题和热重载。按目标平台部署(WASM、iOS、Android、Desktop、Embedded)由 [skill:dotnet-uno-targets] 负责。用于实时文档的 MCP 服务集成由 [skill:dotnet-uno-mcp] 负责。

**不在范围内:**Uno Platform 测试(WASM 的 Playwright、特定平台测试)——请参阅 [skill:dotnet-uno-testing]。通用序列化模式——请参阅 [skill:dotnet-serialization]。WASM 的 AOT/修剪——请参阅 [skill:dotnet-aot-wasm]。UI 框架选择决策树——请参阅 [skill:dotnet-ui-chooser]。

交叉引用:[skill:dotnet-uno-targets](按目标平台部署)、[skill:dotnet-uno-mcp](MCP 集成)、[skill:dotnet-uno-testing](测试模式)、[skill:dotnet-serialization](序列化详解)、[skill:dotnet-aot-wasm](WASM AOT)、[skill:dotnet-ui-chooser](框架选择)、[skill:dotnet-accessibility](无障碍模式;AutomationProperties、WASM 上的 ARIA 映射)。


单项目结构

Uno Platform 5.x 使用单项目结构,通过条件 TFM 实现多目标。一个 .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 Extensions 和主题包。Uno SDK 会自动将这些功能解析为正确的 NuGet 包。

项目布局
MyApp/
  MyApp/
    App.xaml / App.xaml.cs        # Application entry, resource dictionaries
    MainPage.xaml / .xaml.cs      # Initial page
    Presentation/                 # ViewModels or MVUX Models
    Views/                        # XAML pages
    Services/                     # Service interfaces and implementations
    Strings/                      # Localization resources (.resw)
      en/Resources.resw
    Assets/                       # Images, fonts, icons
    appsettings.json              # Configuration (Extensions.Configuration)
    Platforms/                    # Platform-specific code (conditional compilation)
      Android/
      iOS/
      Wasm/
      Desktop/
  MyApp.Tests/                   # Unit tests (shared logic)

Uno Extensions

Uno Extensions 在平台之上提供具有约定的基础设施。所有模块都通过主机构建器模式注册。

主机构建器设置
// 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 声明式驱动,也可通过代码命令式驱动。

// Route registration
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>())
            })
    );
}

<Button Content="View Product"
        uen:Navigation.Request="ProductDetail"
        uen:Navigation.Data="{Binding SelectedProduct}" />

**关键概念:**基于区域的导航会将导航行为附加到可视区域(Frame、NavigationView、TabBar)。路由映射定义导航图。深层链接会将 URL 映射到 WASM 的路由。

依赖注入

包:Uno.Extensions.Hosting

底层使用 Microsoft.Extensions.Hosting。采用主机构建器模式,支持服务注册、键控服务和作用域生命周期。

.ConfigureServices((context, services) =>
{
    // Standard DI registration
    services.AddSingleton<IAuthService, AuthService>();
    services.AddTransient<IOrderService, OrderService>();

    // Keyed services (.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
  }
}
// Binding to strongly-typed options
.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。为 Extensions 生态系统配置 JSON 序列化。

.UseSerialization(configure: serializerBuilder =>
    serializerBuilder
        .AddJsonTypeInfo(AppJsonContext.Default.ProductDto)
        .AddJsonTypeInfo(AppJsonContext.Default.OrderDto))

有关通用序列化模式和 AOT 源生成的详细内容,请参阅 [skill:dotnet-serialization]。

本地化

包:Uno.Extensions.Localization

使用 .resw 文件进行基于资源的本地化,并支持运行时切换区域性。

.UseLocalization()





<TextBlock x:Uid="MainPage_Title" />
<Button x:Uid="MainPage_LoginButton" />

运行时切换区域性:

// Switch culture programmatically
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())

特定平台的接收器:桌面端使用 Debug 输出,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 interface
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(Model-View-Update-eXtended)

MVUX 是 Uno 推荐的响应式模式,与 MVVM 不同。它使用不可变记录、Feeds 和 States,以声明方式对数据流进行建模。源生成器可从普通模型类生成可绑定代理。

核心概念
概念用途MVVM 中的对应项
Model定义 UI 状态的不可变记录ViewModel
Feed异步数据源(加载/数据/错误状态)ObservableCollection + 加载标记
State带有变更跟踪的可变响应式状态INotifyPropertyChanged 属性
ListFeed专用于集合的 FeedObservableCollection
Command从公共异步方法自动生成ICommand
Model 示例
// ProductModel.cs -- MVUX model (source generators produce the bindable proxy)
public partial record ProductModel(IProductService ProductService)
{
    // Feed: async data source with loading/error/data states
    public IFeed<IImmutableList<ProductDto>> Products => Feed
        .Async(async ct => await ProductService.GetProductsAsync(ct));

    // State: mutable reactive value
    public IState<string> SearchTerm => State<string>.Value(this, () => "");

    // ListFeed with selection support
    public IListFeed<ProductDto> FilteredProducts => SearchTerm
        .SelectAsync(async (term, ct) =>
            await ProductService.SearchProductsAsync(term, ct))
        .AsListFeed();

    // Command: auto-generated from async method signature
    public async ValueTask AddProduct(CancellationToken ct)
    {
        var term = await SearchTerm;
        await ProductService.AddProductAsync(term, ct);
    }
}

<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="Error loading products" Foreground="Red" />
                </DataTemplate>
            </FeedView.ErrorTemplate>
        </FeedView>

        <Button Content="Add Product" Command="{Binding AddProduct}" />
    </StackPanel>
</Page>
MVUX 与 MVVM 的比较
关注点MVUXMVVM
Model 定义不可变的 record 类型带有 INotifyPropertyChanged 的可变类
数据加载内置加载/错误状态的 IFeed<T>手动管理加载标记并使用 try/catch
集合带有不可变快照的 IListFeed<T>可变的 ObservableCollection<T>
命令从 async 方法自动生成ICommand 实现(RelayCommand)
状态变更具有显式更新语义的 IState<T>触发 PropertyChanged 的属性 setter
样板代码极少(源生成器)较多(基类、特性)

何时使用 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="Product List"
               Style="{StaticResource HeadlineMedium}" />

    <utu:AutoLayout Spacing="8" Orientation="Horizontal">
        <TextBox PlaceholderText="Search..."
                 utu:AutoLayout.PrimaryLength="*" />
        <Button Content="Search"
                utu:AutoLayout.CounterAlignment="Center" />
    </utu:AutoLayout>

    <ListView ItemsSource="{Binding Products}"
              utu:AutoLayout.PrimaryLength="*" />
</utu:AutoLayout>

主题资源

Uno 将 Material、Cupertino 和 Fluent 设计系统作为主题包提供。主题为所有平台提供一致的颜色、排版、阴影和控件样式。

主题配置

<UnoFeatures>Material</UnoFeatures>   

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            
            <MaterialTheme />

            
            <ResourceDictionary Source="ms-appx:///Themes/ColorPaletteOverride.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>
颜色自定义

通过 ColorPaletteOverride.xaml 覆盖 Material 主题颜色:


<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="Headline" Style="{StaticResource HeadlineMedium}" />
<TextBlock Text="Body text" Style="{StaticResource BodyLarge}" />
<TextBlock Text="Caption" 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 支持
新属性/新方法需要重新生成
资源字典更改完全重新加载
导航路由更改需要重新生成
启用热重载
# Set environment variable before dotnet run
export DOTNET_MODIFIABLE_ASSEMBLIES=debug

# Run with Hot Reload
dotnet run -f net8.0-desktop --project MyApp/MyApp.csproj

Visual Studio 和 VS Code(安装 Uno 扩展后)会自动配置热重载。使用命令行时,请在运行前设置 DOTNET_MODIFIABLE_ASSEMBLIES=debug。

注意: 热重载不支持添加新类型、更改继承层次结构或修改 UnoFeatures。这些操作需要完整重新生成。


Agent 注意事项

  1. 不要混淆 MVUX 和 MVVM。 MVUX 使用不可变记录、Feeds 和 States,而不是 INotifyPropertyChanged。不要向 MVUX 模型添加 ObservableProperty 特性。
  2. 不要为 Uno Extensions 硬编码 NuGet 包版本。 UnoFeatures MSBuild 属性会通过 Uno SDK 自动解析包。为 Extensions 添加显式的 PackageReference 项可能导致版本冲突。
  3. 不要在 Uno XAML 中使用 {Binding StringFormat=...}。 这是 WPF 专属功能。请使用转换器或多个 <Run> 元素来格式化文本。
  4. 不要在绑定中使用 x:Static 或 {x:Reference}。 这些是 WPF 专属的标记扩展,WinUI/Uno 不支持。
  5. 不要设置显式字号或字重。 使用主题的 TextBlock 样式(例如 HeadlineMedium、BodyLarge),以保持设计系统的一致性。
  6. 不要使用硬编码的十六进制颜色。 始终引用主题资源(PrimaryColor、SecondaryColor)或语义画刷,以保持主题兼容性。
  7. 不要在 CommandBar 之外使用 AppBarButton。 独立图标按钮请使用带图标内容的常规 Button。
  8. 不要忘记用于本地化的 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

参考资料

评论

支持 Markdown