这个问题我被问过很多次。说实话,WPF 确实香,但它只香在 Windows 上。一旦你的应用需要跑在 macOS 或 Linux 上,WPF 就彻底哑火了。而 .NET MAUI 虽然支持跨平台,但它对 Linux 桌面的支持至今仍是残缺的。
这就是 Avalonia 的切入点。它用自己的渲染引擎(基于 Skia/Impeller)在每个平台上直接绘制 UI,不依赖平台原生控件,因此视觉效果高度一致。写法上和 WPF 极其相似——XAML + C#,MVVM 模式,绑定、命令、样式,几乎无缝迁移。
更关键的是,随着 .NET 10 的到来,Avalonia 的整个生态已经非常成熟。Avalonia 12.x 系列正在积极跟进 .NET 10 的新特性,性能天花板又被抬高了一截。如果你现在开始一个新的桌面项目,Avalonia 是值得认真考虑的选项。
读完这篇文章,你将掌握:
在真实项目里,跨平台桌面开发的痛苦往往不在于"写不出来",而在于平台差异带来的维护成本。
用 Electron 做跨平台?可以,但一个 Hello World 打包出来就是 150MB 起步,内存占用动辄 200MB+,这在工控、医疗、企业内网这类对资源敏感的场景里根本不现实。用 Qt?需要学 C++,技术栈切换成本极高,而且商业授权也是一笔不小的开销。
Avalonia 的定位恰好填补了这个空缺——原生 .NET、C# 开发体验、自绘 UI 保证跨平台一致性、MIT 开源协议零授权费用。实测数据来看,一个 Avalonia 应用的冷启动内存占用通常在 50~80MB 范围内,打包体积(含运行时)可以控制在 30MB 以内,远优于 Electron 方案。
当然,Avalonia 也有自己的学习曲线。最常见的误区是把它当成 WPF 的完全替代品,直接复制 WPF 代码过来,然后发现一堆命名空间找不到、样式写法不对。Avalonia 是"像 WPF"但不是"等于 WPF",这个认知要提前建立好。
前往 dotnet.microsoft.com 下载 .NET 10 SDK 并安装。安装完成后,在终端验证一下:
bashdotnet --version
# 期望输出类似:10.0.100
这里给出两个主流选择:
如果用 Rider,安装完成后进入 设置 → 插件 → 市场,搜索 AvaloniaRider 并安装,这个插件能让你在写 XAML 的时候实时看到界面预览,省去反复运行的麻烦。
打开终端,执行:
bashdotnet new install Avalonia.Templates
安装完成后,验证模板是否就绪:
bashdotnet new list | grep avalonia
你应该能看到以下几个模板:
| 模板名称 | 短名称 | 说明 |
|---|---|---|
| Avalonia .NET App | avalonia.app | 基础应用模板 |
| Avalonia .NET MVVM App | avalonia.mvvm | MVVM 架构模板(推荐) |
| Avalonia Cross Platform Application | avalonia.xplat | 含移动端/Web 的全平台模板 |
bashdotnet new avalonia.mvvm -o AppMyFirstAvalonia
cd AppMyFirstAvalonia
或者

项目创建完成后,目录结构大致如下:
AppFirstAvalonia/ ├── App.axaml # 应用程序入口定义(相当于 WPF 的 App.xaml) ├── App.axaml.cs # App 后台代码 ├── Assets # 资源文件目录 ├── MainWindow.axaml # 主窗口 UI 定义 ├── MainWindow.axaml.cs # 主窗口后台代码(通常很薄) ├── ViewModels/ │ ├── MainWindowViewModel.cs # 主窗口的 ViewModel │ └── ViewModelBase.cs # ViewModel 基类(实现 INotifyPropertyChanged) ├── Views/ │ └── MainView.axaml # 主视图(内容区域) └── Program.cs # 程序入口
注意 Avalonia 用的文件扩展名是 .axaml,不是 .xaml,这是为了让工具链能区分 Avalonia 和 WPF 的 XAML 文件。内容格式是完全相同的,不用担心。
Program.cs 是整个应用的入口,内容非常简洁:
csharpusing Avalonia;
using System;
namespace AppMyFirstAvalonia
{
internal sealed class Program
{
//程序入口,[STAThread] 确保 UI 线程为单线程套间
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// 配置 Avalonia 应用,使用平台默认主题, 不要动这块
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
#if DEBUG
.WithDeveloperTools()
#endif
.WithInterFont()
.LogToTrace();
}
}
App.axaml 负责注册视图和主题:
xml<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AppMyFirstAvalonia.App"
xmlns:local="using:AppMyFirstAvalonia"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<!-- 使用 Fluent 主题,支持 Light/Dark 自动切换 -->
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
光看结构没意思,咱们直接动手做一个有实际交互的功能——点击按钮计数,并在界面上实时显示。这个小案例能把 Avalonia 的数据绑定和命令机制串联起来,是理解 MVVM 的最佳入口。
打开 ViewModels/MainWindowViewModel.cs,替换为以下内容:
csharpusing CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace AppMyFirstAvalonia.ViewModels
{
// ObservableObject 来自 CommunityToolkit.Mvvm,自动实现 INotifyPropertyChanged
public partial class MainWindowViewModel : ObservableObject
{
// [ObservableProperty] 特性会自动生成 Count 属性和 OnCountChanged 通知
[ObservableProperty]
private int _count = 0;
// 显示在界面上的消息,依赖 Count 变化
public string StatusMessage => Count == 0
? "点击按钮开始计数"
: $"你已经点击了 {Count} 次";
// RelayCommand 自动生成 IncrementCommand
[RelayCommand]
private void Increment()
{
Count++;
// 通知 StatusMessage 也更新
OnPropertyChanged(nameof(StatusMessage));
}
[RelayCommand]
private void Reset()
{
Count = 0;
OnPropertyChanged(nameof(StatusMessage));
}
}
}
💡 踩坑预警:Avalonia 的 MVVM 模板默认引入了
CommunityToolkit.Mvvm包,直接用就好。如果你发现[ObservableProperty]特性报错,检查一下项目文件里是否有<PackageReference Include="CommunityToolkit.Mvvm" />,没有的话手动添加。
打开 Views/MainWindow.axaml,写入以下界面定义:
xml<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:AppMyFirstAvalonia.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AppMyFirstAvalonia.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="AppMyFirstAvalonia">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<!-- 垂直居中布局 -->
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="16">
<!-- 大号数字显示计数 -->
<TextBlock Text="{Binding Count}"
FontSize="72"
FontWeight="Bold"
HorizontalAlignment="Center"
Foreground="#2196F3"/>
<!-- 状态提示文字 -->
<TextBlock Text="{Binding StatusMessage}"
FontSize="14"
HorizontalAlignment="Center"
Foreground="#757575"/>
<!-- 按钮组 -->
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="12">
<Button Content="点击 +1"
Command="{Binding IncrementCommand}"
Width="120"
Height="40"
FontSize="15"/>
<Button Content="重置"
Command="{Binding ResetCommand}"
Width="80"
Height="40"
FontSize="15"/>
</StackPanel>
</StackPanel>
</Window>
注意 x:DataType="vm:MainWindowViewModel" 这行——这是 Avalonia 的编译时绑定声明,它让 IDE 能在你写 {Binding Count} 的时候给出智能提示,并在编译期就发现拼写错误,而不是等到运行时才崩溃。这是 Avalonia 相比 WPF 在开发体验上的一个明显进步,强烈建议养成习惯。

片刻之后,一个窗口会弹出来:中间显示大号的 0,下面是提示文字,两个按钮点击后数字实时变化。整个过程没有写一行事件处理代码,数据绑定和命令机制把一切都接管了。
以下数据基于同等功能的简单窗口应用,在 Windows 11 / Intel i7-12700H / 16GB RAM 环境下测试:
| 框架 | 冷启动时间 | 初始内存占用 | 打包体积(含运行时) |
|---|---|---|---|
| Avalonia + .NET 10 | ~280ms | ~55MB | ~28MB |
| WPF + .NET 10 | ~210ms | ~48MB | ~25MB |
| Electron 30 | ~1800ms | ~210MB | ~160MB |
| .NET MAUI (Win) | ~350ms | ~70MB | ~35MB |
WPF 在 Windows 上略有优势,但一旦涉及跨平台需求,Avalonia 是目前 .NET 生态里性能最接近原生、开发体验最流畅的选择。
坑一:XAML 命名空间写错。Avalonia 的根命名空间是 https://github.com/avaloniaui,不是 WPF 的 http://schemas.microsoft.com/winfx/2006/xaml/presentation。从 WPF 迁移时这个最容易忘,结果就是整个界面白屏,没有任何报错提示。
坑二:忘记声明 x:DataType。不写这个,编译时绑定就退化为运行时绑定,丢失了 IDE 的类型检查能力,而且在某些场景下会有轻微性能损耗。
坑三:在非 UI 线程更新属性。Avalonia 和 WPF 一样,UI 更新必须在主线程上进行。如果你在 Task.Run 里修改了绑定属性,需要用 Dispatcher.UIThread.InvokeAsync 来调度回主线程:
csharp// 在后台线程中更新 UI 属性的正确姿势
await Dispatcher.UIThread.InvokeAsync(() =>
{
Count = newValue;
});
坑四:样式作用域问题。Avalonia 的样式系统和 WPF 有细微差别,Style 的 Selector 语法不同(类似 CSS 选择器),直接把 WPF 的样式代码粘贴过来会直接报错。建议从官方文档的样式章节单独学习这块。
"Avalonia 是 WPF 的跨平台进化版,不是替代品,是延伸。"
"编译时绑定(x
)是 Avalonia 最值得养成的开发习惯之一。"
"一套 C# 代码,三个平台同时跑,这在 .NET 生态里终于不再是奢望。"
掌握了这篇文章的内容之后,建议按以下路径继续深入:
FluentTheme 的深度定制;ReactiveUI 或 Avalonia.Navigation,实现多视图切换;ItemsControl、DataGrid 的高性能用法,掌握虚拟化机制;dotnet publish 的 --self-contained 选项,以及 Velopack 等自动更新方案。你在实际项目中有没有遇到过"需要跨平台但又不想放弃 C# 生态"的困境?当时是怎么解决的?欢迎在评论区聊聊你的选型思路,或者分享一下你在 Avalonia 或 MAUI 上踩过的坑,说不定能帮到正在纠结的人。
#C#开发 #Avalonia #跨平台桌面 #.NET10 #MVVM #性能优化
相关信息
我用夸克网盘给你分享了「AppMyFirstAvalonia.zip」,点击链接或复制整段内容,打开「夸克APP」即可获取。 链接:https://pan.quark.cn/s/5cc232cf0b62 提取码:iduG
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!