详情

首页手游攻略 Avalonia.Markup.Declarative:实践指南

Avalonia.Markup.Declarative:实践指南

佚名 2026-09-11 09:20:01

工作中遇到相关需求时,Avalonia.Markup.Declarative值得先读说明,因为它主要用于为 C# 中的声明式 ui 提供帮助程序。对界面与前端开发任务来说,视觉还原、交互状态和响应式细节容易遗漏往往决定它能否落地,不能只用安装成功来判断。落地前可以选一个包含多状态的真实组件完成实现,用尺寸、状态、可访问性、资源和不同视口表现判断它是否真的省事。对愿意逐状态验收界面质量的前端团队来说,这个仓库值得继续验证;只求即装即用的人则要先看维护成本。

免责声明

重要提示: 该存储库是社区驱动的,不受 avalonia 团队的正式支持,也不属于官方 Avalonia 项目的一部分 - 它只是一个概念证明,演示如何纯粹用 C# 编写标记。对于实际项目,请使用 Avalonia 支持的 XAML 方法。

Avalonia.Markup.声明式

像老板一样用 C# 编写 Avalonia UI

Avalonia.Markup.Declarative 是 Avalonia 控件上的 C# 优先创作层。当前的 API 是编译绑定优先和源生成器驱动的,其公共模式有意与 Avalonia 的 DataContext、绑定、样式和选择器模型保持一致。

使用 Avalonia.Markup.Declarative 的真实项目

https://github.com/gritsenko/pix2d - 面向独立开发人员的 Pix2d 图形编辑器

随意添加您的项目

安装

Avalonia.Markup.Declarative NuGet 包添加到您的项目中

项目模板

您可以使用官方模板从命令行轻松创建新项目:

dotnet new install Declarative.Avalonia.Templates
dotnet new avalonia-declarative -n MyApp

声明性组件模式(单个文件 component/SFC)

当视图及其反应状态属于同一功能时,请使用独立的声明性组件。将 CommunityToolkit.Mvvm 添加到应用程序项目,并将组件本地状态保留在嵌套的 ObservableObject 中。

using Avalonia.Data;
using CommunityToolkit.Mvvm.ComponentModel;

public class CounterComponent() : ViewBase<CounterComponent.State>(new State())
{
    public sealed partial class State : ObservableObject
    {
        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(CounterLabel))]
        public partial decimal? Counter { get; set; } = 0;

        [ObservableProperty]
        public partial string StatusText { get; set; } = "Hello world";

        public string CounterLabel => $"Counter: {Counter}";
    }

    protected override object Build(State state) =>
        new StackPanel()
            .Children(
                new TextBlock()
                    .Text(state, x => x.StatusText),
                new TextBlock()
                    .Text(state, x => x.CounterLabel),
                new NumericUpDown()
                    .Value(state, x => x.Counter, BindingMode.TwoWay),
                new Button()
                    .Content("Increment")
                    .OnClick(_ => state.Counter++)
            );
}

要编写构造函数注入视图,首选 ViewFactory.Create<T>()。如果您使用DI,请在AppBuilder上注册UseComponentControlFactory(...)

MVVM 模式实现

当您想要经典的 Avalonia 或 WPF-style 视图模型时,请使用 ViewBase<TViewModel>。生成的 setter 公开编译绑定重载,因此绑定语法与本机 Avalonia 保持接近。

using Avalonia.Data;

public class MainView() : ViewBase<MainViewModel>(new MainViewModel())
{
    protected override object Build(MainViewModel vm) =>
        new StackPanel()
            .Children(
                new TextBox()
                    .Text(vm, x => x.Message, BindingMode.TwoWay),
                new TextBlock()
                    .Text(vm, x => x.Message),
                new Button()
                    .Content("Reset")
                    .OnClick(_ => vm.Message = string.Empty)
            );
}

相同视图的等效 XAML:

<UserControl
    xmlns="https://github.com/avaloniaui"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="using:MyApp.ViewModels"
    x:Class="MyApp.MainView"
    x:DataType="vm:MainViewModel">
    <StackPanel>
        <TextBox Text="{CompiledBinding Message, Mode=TwoWay}" />
        <TextBlock Text="{CompiledBinding Message}" />
        <Button Content="Reset"
                Click="ResetClick" />
    </StackPanel>
</UserControl>

如果从外部分配 DataContext,则相同的生成器还支持 DataContext-relative 编译绑定,例如 new TextBlock().Text<MainViewModel>(x => x.Message);

生成的编译绑定 setter 还对常见原语和可为 null 的不匹配应用自动转换,因此当 Counterint 时,像 new Slider().Value(vm, x => x.Counter, BindingMode.TwoWay) 这样的绑定可以工作,而当 Enabledbool 时,new CheckBox().IsChecked(vm, x => x.Enabled) 可以工作。更喜欢普通成员访问,例如 x => x.Counter:像 x => (double)x.Counter 这样的数字转换强制转换既不必要(自动转换器处理它),又不受支持 — Avalonia 的表达式解析器拒绝值转换 Convert 节点。 *支持导航到派生类型成员的类型转换,例如 x => ((DerivedType)x).Property。对于有损数字 TwoWay 转换,转换回会截断为零。

传递现成的绑定

当您需要完整的绑定功能集 - 反射绑定 (Binding)、预构建的编译绑定、TemplateBindingMultiBinding 或 relative-source/element-name 绑定 - 每个生成的属性、附加属性和样式设置器还会公开接受 BindingBase 的重载直接:

using Avalonia.Data;

new TextBlock()
    .Text(new Binding("ReflectionProperty"))                                // DataContext-relative reflection binding
    .Foreground(new Binding("Theme.Accent") { Source = appState });         // explicit source

new TextBlock()
    .Text(CompiledBinding.Create<MyViewModel, string>(x => x.Title, source: vm));

// attached properties and styles get the same overload
new Border().Grid_Row(new Binding(nameof(vm.Row)) { Source = vm });
new Style<TextBlock>().Text(new Binding(nameof(vm.Name)));

这是强类型 x => x.Member 表达式重载未涵盖的任何内容的逃生口(在绑定上传递的自定义转换器、RelativeSourceElementName、字符串格式、多值绑定等)。通过 control.BindValue(TextBlock.TextProperty, binding) 在任何 AvaloniaProperty 上都可以使用相同的功能。

热重载支持

  • ViewBase 支持.NET 6.0+ 热重载。

  • 在没有 XAML 的程序集中保留声明性视图仍然可以产生最流畅的热重载体验。

  • 当前的 Avalonia 和 .NET 工具链在同一应用程序中混合 AXAML 和 C# 标记方面要好得多,因此限制比以前小得多。

  • 要显式启用 AMD 热重载集成:

    AppBuilder.Configure<Application>()
      .UseHotReload()
      .SetupWithLifetime(lifetime);
    

AI代理工装(MCP)

可选的 仅限开发 Declarative.Avalonia.AgentTools 包运行进程内 MCP (模型 上下文协议)服务器在调试版本中的环回上,因此在 UI 上迭代的 AI 代理可以查看并 驱动正在运行的应用程序 — 关闭编辑 → 热重载 → 验证循环,无需人工中继 窗口看起来像。

using Declarative.Avalonia.AgentTools; // under #if DEBUG

AppBuilder.Configure<App>()
    .UsePlatformDetect()
#if DEBUG
    .UseAgentInspector() // loopback MCP server on 127.0.0.1:5599
#endif
    .SetupWithLifetime(lifetime);

它公开了屏幕截图(使用 before/after 像素差异)、具有边界的视觉树和单个 绝对客户端-DIP 坐标系 (abs/center) 由每个工具共享,每个控件布局报告, 自动布局审核、属性/属性源/视图模型检查、像素↔控制命中测试 (带有“如何驱动此控件”提示)以及最近的 build/binding/runtime 错误。选择加入层 (EnableInteraction) 还驱动应用程序 — 真实合成的 pointer/keyboard 输入 (tapdragpointer_*,甚至可以在没有自动化对等的自定义控件上工作),单击、键入、选择、调整大小, 切换主题,打开一个关闭的弹出窗口 - 加上一个逃生舱口(带有结构化的、可操作的错误)来设置 查看模型属性或直接运行命令以达到尴尬的状态。

将调用保留在 #if DEBUG 下:该包会拉入 Web 堆栈和远程控制界面,并且不得 发布中的船舶;它仅绑定到环回。完整信息请参见 docs/agent-tools.md 指导。

在代理中启用 MCP

检查器是 http://127.0.0.1:5599 上的可流式-HTTP MCP 服务器,因此每个代理都指向 相同的URL。在 dotnet watch 下运行应用程序,以便代理的编辑热重载到它检查的进程中。

克劳德代码claude mcp add --transport http avalonia-agent-inspector http://127.0.0.1:5599,或 项目 .mcp.json

{ "mcpServers": { "avalonia-agent-inspector": { "type": "http", "url": "http://127.0.0.1:5599" } } }

opencode — 在 opencode.json 中的 mcp 下(远程服务器使用 type: "remote"):

{ "mcp": { "avalonia-agent-inspector": { "type": "remote", "url": "http://127.0.0.1:5599", "enabled": true } } }

Codex~/.codex/config.toml 中的 [mcp_servers.*] 表(url 使其可流化-HTTP;启用 RMCP 客户端使用 [features]experimental_use_rmcp_client = true):

[mcp_servers.avalonia-agent-inspector]
url = "http://127.0.0.1:5599"

请参阅 docs/agent-tools.md 了解标头、项目范围的变体、 以及完整的工具参考。

为您自己和外部控件生成源代码

该软件包附带了源生成器,该生成器可生成以下标记扩展:

  • 引用Avalonia框架组件
  • 在您自己的项目中声明的控件
  • 选择加入的第三方程序集

如果您下载了源代码或克隆了此存储库,请将源生成器项目添加为分析器参考:

    <ItemGroup>
        <ProjectReference Include="....Avalonia.Markup.Declarative.SourceGeneratorAvalonia.Markup.Declarative.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    </ItemGroup>

确保源生成器项目的路径相对于您的项目是正确的。

注意:如果您将此库用作 NuGet 包,则会自动包含源生成器。

外部库支持

框架扩展会自动为您的项目引用的受支持的 Avalonia 程序集生成。要为第三方库生成扩展,请添加指向该程序集中任何类型的程序集属性:

using Avalonia.Markup.Declarative;
using ReactiveUI.Avalonia;

[assembly: GenerateMarkupExtensionsForAssembly(typeof(RoutedViewHost))]

不再需要独立工具安装或手动 avalonia-amd-gen 步骤。

相关资讯
点击查看更多
游戏推荐
推荐专题
热门阅读
推荐下载