
第一次创建Avalonia v11项目时我遇到的第一件事不是编译错误而是设计器空白。打开MainWindow.axaml左侧是AXAML源码右侧本该出预览的区域一直在加载转了半天圈之后干脆给你一片空白。这个画面劝退了不少新手但其实绕过它并不难。这篇文章是我自己从零创建第一个Avalonia v11跨平台桌面App的完整记录包括环境配置、模板创建、AXAML结构以及很多人问得最多的“设计器不显示怎么解决”。Avalonia v11是当前.NET生态里少有的能同时覆盖Windows、Linux、macOS的桌面UI方案特别适合做工具类软件、上位机程序和多平台产品。如果你有WPF基础这篇可以直接当作Avalonia的快速上手指南即使完全没接触过XAML照着命令和代码走一遍也能把第一个窗口跑起来。1. Avalonia不是又一个WPF换皮v11这次改的是底子1.1 它到底是怎么实现跨平台的Avalonia是一个基于.NET的跨平台UI框架官方口号是“Write Once, Run Anywhere”但它的实现方式和Java那套虚拟机完全不同。你可以简单理解为Avalonia用XAML描述界面用C#承载业务逻辑最终由程序集自带的渲染层直接绘制到目标系统窗口上。和WPF的关系是——语法和API大量借鉴了WPF有DataContext、有Binding、有ControlTemplate、有Style这让很多WPF老手能快速上手。但它不是WPF的复制品更不是换个壳接着用。WPF的渲染管道依赖DirectX并且和.NET Framework深度绑定后面又和.NET Core纠缠了很久Avalonia从根上就走了一条更“野”的路自己管理渲染树把Skia作为光栅化后端再在每个平台对接窗口系统和输入事件。用一个生活类比WPF像装修公司只服务Windows小区设计图纸、家具、施工队都是Windows限定Avalonia则像一支流动装修队图纸格式用同一套但能进Windows、Linux、macOS的小区干活而且每个小区的墙面和门窗它都有对应的适配方案。所以同样的界面代码放到三个系统上视觉和交互呈现非常接近字体、圆角、阴影这些细节不会因为操作系统不同而突然变了个样。1.2 v11相对早期版本的几个关键变化Avalonia的版本演进里v0.10到v11是一个跨度很大的过程很多人从旧文档迁移过来时会明显感到差异。v11主要改了几件事渲染合成器升级支持Acrylic、Mica这些现代材质效果窗口背景不再只是纯色或者一张位图。控件命名空间做了整理不少控件从Avalonia.Controls移到了Avalonia.Controls.Primitives用旧版代码时编译会报错网上搜到的大多是10.x的答案照着改还得动不少脑筋。资源系统增强了支持基于类型的资源定义样式里BasedOn、Classes这些用法更灵活。动态资源和主题切换比旧版稳定很多深色/浅色主题切换不再出现某些控件颜色不刷新的问题。对ReadyToRun、单文件发布、剪裁的支持更成熟很多做分发的人终于不用再和一大堆DLL搏斗。模板和设计时支持也更新了VS预览器在新版本里明显比早期版本稳但还没到“装好即所见”的程度这正是后面要详细讲设计器排查的原因。如果你是从10.x升上来的建议直接看官方迁移文档不要心存侥幸。我见过太多人把老项目直接改引用版本号结果预览器打不开、运行时控件全部丢失最后都得回到迁移文档一步一步改。1.3 WPF、MAUI、Avalonia框架选型对照选型之前先看清楚自己的目标运行平台。我直接用一张表对比三个主流.NET桌面框架框架支持平台渲染方式学习成本适合场景WPF仅WindowsDirectX低有大量资料Windows独占桌面应用、老项目维护MAUIWindows/macOS/iOS/Android原生控件映射中高平台差异多需要移动端桌面端一体的团队AvaloniaWindows/macOS/LinuxSkia自绘中WPF基础可迁移工具软件、上位机、三平台桌面应用选型时别只看框架本身还要看团队技能树和产品交付目标。你已经用WPF做了很多年Windows端工具并且没有Linux需求保持WPF是理性的如果要三平台桌面又不想为每个平台重写一套界面Avalonia是目前.NET生态里比较顺手的MAUI把移动端也纳入但桌面的控件成熟度和第三方生态还需要时间。我个人的看法是做纯桌面工具链Avalonia v11现在可以放心用做金融交易终端、工控上位机、音视频工具这类需要长时间稳定运行的软件它已经有不少实际生产案例了。2. 环境准备把这几样装对了后面至少省一半时间2.1 确认.NET SDK版本Avalonia 11通常要求.NET 6以上的运行时但我的建议是直接用SDK 8.0 LTSSDK 9同样也能跑通。为什么强调装SDK而不是运行时因为项目模板创建、dotnet publish、使用MSBuild任务等都需要完整SDK只装运行时的话后面哪一步都可能卡住。装完后在终端执行dotnet --version确认输出的是8.x或更高再看一眼dotnet --list-sdks有没有多个版本。多版本本身没问题但要注意全局SDK版本别选到太旧的。如果你的机器上还同时装了Visual Studio自带的.NET SDK命令行默认走的可能不是你预期的那一个这时候在csproj的global.json里固定版本最省心。2.2 安装IDE扩展Visual Studio 2022用户直接打开“扩展”菜单搜索“Avalonia”安装“Avalonia for Visual Studio”扩展。注意这里有几个容易忽略的细节安装完成后必须完全关闭并重启Visual Studio扩展才会生效。扩展管理器里可能同时存在老版本的Avalonia扩展务必选择对应v11的版本否则后面设计器会出各种问题。确认VS版本在17.4以上太老的VS和新的Avalonia设计器组件不匹配安装时可能会警告但不拦你实际用起来到处是坑。如果你用的是Rider情况会省心很多。Rider对AXAML有内置支持XAML预览、代码补全、导航都做得比较完整基本不用额外装插件。VS Code用户也可以开发但设计器基本指望不上只能靠运行时预览来调UI所以建议新手优先用VS或Rider。2.3 用dotnet new安装模板并创建项目Avalonia项目模板也是通过NuGet分发的首先安装模板包dotnet new install Avalonia.Templates装完可以验证一下dotnet new list | findstr avalonia能看到avalonia.app、avalonia.mvvm、avalonia.xplat这些模板名称。创建工程dotnet new avalonia.app -o DemoApp cd DemoApp dotnet run这里要提醒一下dotnet new install是.NET 6之后才有的语法早先版本用的是dotnet new -i。如果你复制某篇老博客的命令发现报错先检查这个。第一次运行时会从NuGet拉取Avalonia相关包耐心等一会儿窗口弹出来看到熟悉的“Hello World”界面项目骨架就算跑通了。2.4 模板、NuGet包、插件三者的版本一致性这是我最想强调的一个隐形坑模板版本、项目引用的Avalonia NuGet包版本、Visual Studio插件版本三者最好保持一致。我第一次上手时模板创建出来的csproj引用的是11.0.x而VS插件还是老的0.10.x结果设计器完全不加载。当时完全没往版本方向想以为是自己的显卡驱动或者渲染设置问题折腾了很久。后来把插件升级到11.x问题才消失。从原理上讲设计器需要在一个设计时进程里加载项目的编译结果以及Avalonia程序集如果插件、NuGet包、模板三方版本不一致加载阶段就会触发程序集版本冲突设计器当然只能空转或直接退出。排查的时候先打开csproj看一眼PackageReference的版本再看扩展管理器里插件的版本两边的大版本号必须一致。小版本差几个build通常没问题但大版本差了基本都会出问题。3. 从模板到第一个窗口AXAML项目结构拆解3.1 模板生成的目录结构创建好的项目结构如下DemoApp/ ├── App.axaml ├── App.axaml.cs ├── MainWindow.axaml ├── MainWindow.axaml.cs ├── Program.cs ├── DemoApp.csproj └── Assets/ └── avalonia-logo.ico逐个文件说一下作用Program.cs程序入口创建App类并启动应用。App.axaml应用级资源和主题设置相当于WPF里的App.xaml。App.axaml.cs应用启动逻辑模板里就是调用MainWindow。MainWindow.axaml主窗体的XAML。MainWindow.axaml.cs主窗体的代码后置事件处理和业务逻辑都写在这。熟悉WPF的人看到这个结构会非常有安全感它几乎就是WPF项目结构的复刻。不过也有区别Avalonia把资源和样式都视为可独立组织的对象你可以自己建Styles/目录、Resources/目录在App.axaml里统一引用。模板只是给了最小骨架实际项目完全可以根据团队习惯拆分。3.2 MainWindow.axamlAXAML基础语法模板生成的MainWindow.axaml内容大致如下Window xmlnshttps://github.com/avaloniaui xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:dhttp://schemas.microsoft.com/expression/blend/2008 xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006 mc:Ignorabled d:DesignWidth800 d:DesignHeight450 x:ClassDemoApp.MainWindow TitleDemoApp Width800 Height450 StackPanel Spacing16 HorizontalAlignmentCenter VerticalAlignmentCenter Button x:NameGreetButton Content点击我 ClickOnGreetButtonClick/ TextBlock x:NameResultText Text还没有点击/ /StackPanel /Window注意根元素的命名空间https://github.com/avaloniaui而不是WPF那套schemas.microsoft.com。这个URL本质是XML命名空间标识符不会真的去访问GitHub离线开发完全不用担心。mc:Ignorabled表示d:前缀的属性只在设计时生效运行时忽略。d:DesignWidth和d:DesignHeight就是设计器里预览画布的大小这两个属性不影响运行时窗口真实尺寸。x:Class指向编译后的类名必须和代码后置里的类声明保持一致不一致时设计器会直接报错。布局用的StackPanel从上到下排列元素Spacing是间距HorizontalAlignment和VerticalAlignment控制对齐方式。这些概念WPF用户闭着眼都认识所以从WPF转过来基本是“平移知识”的过程区别只在细节。3.3 按钮事件最小交互闭环在MainWindow.axaml.cs里补上事件处理using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; namespace DemoApp; public partial class MainWindow : Window { private int _count; public MainWindow() { InitializeComponent(); } private void OnGreetButtonClick(object? sender, RoutedEventArgs e) { _count; ResultText.Text $点击了 {_count} 次; } }和WPF很像但要注意事件参数类型是RoutedEventArgs而且InitializeComponent()这种经典方法在Avalonia里依然存在。编译运行后点按钮看文本更新这个最小闭环分别在Windows、Linux、macOS上跑一遍就是一次完整的“跨平台Demo”体验了。我做这个Demo时特意在Linux虚拟机上跑了同一个项目视觉效果和Windows几乎一致连按钮的圆角和阴影都一样。这就是自绘渲染的好处不依赖系统控件各平台展示结果非常统一。3.4 布局、样式和资源够用就好新手阶段别一上来就玩复杂的ControlTemplate和样式触发器先把三种布局控件用熟Grid行列布局适合做表单和复杂界面骨架。StackPanel垂直或水平堆叠适合简单列表面板。Border给控件加边框、圆角、背景是美化界面的基础。样式可以放在Window.Resources或App.Resources里。Avalonia的样式选择器和WPF有差异WPF用TargetType加SetterAvalonia则常用Classes和Selector。简单场景下直接用Control的Background、Foreground属性就够了。等第一个窗口跑通再逐步把界面拆成UserControl和独立资源文件这样前期学习曲线比较平缓。4. 设计器不显示排查全链路4.1 同一问题三种常见表象设计器不显示不是一个单一报错你可能会看到三种情况打开.axaml文件右侧预览区域一直显示“Loading”转圈很久后空白。设计器直接显示“An exception occurred while loading the document”之类的错误。干脆没有设计器面板只有一个纯文本模式右键也没有“打开预览器”选项。这三种表象对应的原因不太一样但排查思路是共通的。如果你用命令行dotnet run能正常弹出窗口说明项目本身没问题设计器只是工具链层面的问题。4.2 按顺序检查的排查清单我习惯按下面这个顺序过一遍效率最高检查项操作方法说明插件是否装好并启用扩展管理器搜索“Avalonia”安装后需重启VS禁用状态会直接导致无设计器插件与包版本大版本是否一致查看扩展版本和csproj里的PackageReference大版本不一致比如插件10、包11基本必挂项目能否编译通过按CtrlShiftB编译不过设计器必然失败优先解决编译错误csproj里Avalonia包版本是否统一打开csproj逐项检查多个Avalonia包版本混用也会引发加载异常清理解析缓存关闭VS删除bin、obj、.vs目录旧编译产物可能干扰设计时加载清理VS组件模型缓存删除%LocalAppData%\Microsoft\VisualStudio\17.0_xxx\ComponentModelCache扩展加载异常时有效查看Avalonia输出日志菜单“视图→输出”窗口下拉选“Avalonia”往往有具体的异常堆栈检查构造函数是否有阻塞逻辑看MainWindow构造函数是否做了重活设计器需要实例化窗体构造函数卡住或抛异常都会导致预览失败上面这个表看起来内容很多实际排查时间通常在十分钟以内。最经常命中是第一种和最后一种。4.3 最容易忽略的根因构造函数拖垮设计时实例化设计器看起来是“预览”本质上是把项目的程序集加载到一个设计时宿主进程里然后实例化你打开的视图。这意味着MainWindow的构造函数会被真实执行。很多人的构造函数里写了大量初始化比如读取数据库、new一个IoC容器、启动后台任务、订阅外部消息。这些逻辑在运行时没问题但设计器一旦执行到就会抛异常或者卡住于是你看到的不是“报错字体变红”而是设计器区域永远空白。这比版本不匹配更难排查因为它不产生直接的AXAML错误。解决办法有两个方向。一是把重量级初始化移到OnLoaded或窗口显示后再做二是判断当前是否处于设计时模式直接跳过这些初始化using Avalonia.Controls; public MainWindow() { InitializeComponent(); if (Design.IsDesignMode) { return; } // 业务初始化放这里 LoadData(); }Avalonia.Controls.Design这个静态类就是为此准备的。它还有Design.DataContext、Design.Width、Design.Height这些设计时辅助属性。你还可以在AXAML里给d:DataContext绑定一个假数据源让设计器在无运行时依赖的情况下渲染出有数据的界面这对调试列表模板非常有用。4.4 终极兜底把运行时当成设计器如果上面所有检查都做完设计器还是顽固地不显示我不建议继续死磕。我的兜底方案一直是把运行时当成设计器直接用dotnet run调界面。运行时看到的是真实渲染结果比设计器预览还准确因为它就是最终用户看到的画面。为了让这个流程顺手一点可以写一个简单脚本dotnet build if ($?) { dotnet run --no-build }保存成run.ps1放项目根目录每次改完AXAML就执行一下几秒钟后窗口起来直接看效果。配合系统自带的窗口位置记忆工具或者多开几个终端调试效率并不比设计器低。我在实际项目中即使设计器能正常显示也会定期用运行时验证一遍因为光照、字体、DPI这些效果只有运行时才完全真实。4.5 排查完才知道的结论回头看设计器不显示这个问题的80%都指向两个原因一是插件版本和Avalonia NuGet包版本不一致二是构造函数在设计时被实例化时挂掉了。剩下的20%才是VS组件模型缓存损坏、扩展冲突这些小概率事件。把第4.2节的表格完整过一遍基本都能定位。还有一个很容易被忽略的小知识设计器进程加载项目依赖时需要访问NuGet包源如果你的网络策略禁用了NuGet或者本机NuGet缓存损坏设计器也会失败。这和在“离线环境开新项目”时特别常见。只要记住“项目能跑设计器只是额外工具”就不会在这个问题上钻牛角尖。5. 让同一个App在Windows、Linux、macOS上跑起来5.1 跨平台不是Web套壳很多人会怀疑Avalonia是不是类似Electron那种套浏览器内核的方案其实不是。Avalonia是原生编译到目标平台进程的UI由Skia直接绘制每个控件的布局和渲染都在. NET进程内完成并不存在一个隐藏的浏览器页面。这也解释了为什么Avalonia应用的启动速度和内存占用比Electron好不少同时又保持跨平台一致性。在项目结构上你只需要一份代码、一份AXAML发布时针对不同平台指定不同的运行时标识符RID即可。代码里如果使用平台相关API比如读取Windows注册表需要自己用条件编译或依赖注入隔离这是跨平台开发的通用课题。5.2 三个平台的单文件发布命令发布到三个平台时我常用这三条命令dotnet publish -c Release -r win-x64 --self-contained true dotnet publish -c Release -r linux-x64 --self-contained true dotnet publish -c Release -r osx-x64 --self-contained true如果是Apple Silicon Mac把osx-x64换成osx-arm64。--self-contained true意思是把.NET运行时打包进去目标机器不用装.NET也能跑。默认发布出来是文件夹加一堆DLL想压成单文件可以再加/p:PublishSingleFiletrue但要注意有些原生库比如Skia相关DLL在单文件模式下可能需要额外的提取配置我建议先把普通发布跑通再折腾单文件发布。这里有一个体验不错的细节同一套代码发布出来的Windows版本和Linux版本只要没有用到平台专属库运行时行为几乎一致。我在Linux上调试遇到的UI问题回到Windows基本都不复现反过来也一样。5.3 Linux运行时依赖与中文字体在Linux上跑Avalonia最常见的问题是缺系统库。纯净服务器环境直接运行编译好的程序经常会因为缺少libfontconfig、libICE等而启动失败。以Ubuntu/Debian为例可以提前装这些基础包sudo apt install libfontconfig1 libice6 libsm6 libgl1-mesa-glx包名在不同发行版略有差异Arch上名字可能不一样但对应的库就这些。比缺库更隐蔽的是字体问题。Avalonia不会把中文字体打包进应用它依赖系统fontconfig来解析字体。如果目标Linux系统里没有任何中文字体界面上的中文会全部变成方框。这个坑我踩过好几次解决办法也很直接——装一个CJK字体sudo apt install fonts-noto-cjk装完重跑应用中文就正常了。做交付的时候这一步要写进部署文档否则客户拿到Linux版本跑起来全是方框第一反应就是“你的程序有bug”。5.4 macOS的签名问题和分发姿势macOS下发布的是.app文件夹结构dotnet publish输出目录里会出现DemoApp.app。本机运行没什么问题但把应用发给别人时未签名应用会被Gatekeeper拦截别人右键选择“打开”也未必能绕过去。最简单的临时方案是执行完发布后在终端对应用做一下移除隔离标记xattr -cr path/to/DemoApp.app这是开发阶段分发给同事验证用的办法正式对外发布还是老老实实申请Apple Developer ID用codesign签名再进行公证Notarization否则用户下载后大概率会卡在系统安全提示上。5.5 版本升级的时机最后一个建议现在上手直接用Avalonia 11稳定版不要为了尝鲜去追预览版也不要用旧教程里的10.x版本。11已经有了相对完整的文档、模板、插件支持社区提问也多集中在11版本的语境下。后续升级大版本时不要只升级NuGet包而是要记住“模板、SDK、插件、NuGet包”四件套一起行动缺一个都可能重现设计器不显示、样式加载异常这类问题。把csproj里的版本号锁住别让CI自动拉取到奇怪的预发布版本生产项目经不起这种惊喜。最后说点个人的体会。我当时第一次创建Avalonia项目时设计器同样不显示也怀疑过是环境问题还是自己写错甚至把扩展装了三遍。最后发现只是插件版本太旧和v11的项目版本对不上。升级插件之后设计器确实恢复了但我已经习惯直接按F5跑起来看效果反而觉得运行时预览更真实。设计器是效率工具不是必需品跨平台桌面开发的真正核心是把项目骨架、AXAML组织方式和发布链路摸熟。Avalonia v11现在值得上手它给.NET开发者提供了一条不依赖Windows也能做出原生级桌面应用的路子。如果你在创建项目时遇到设计器空白别急着怀疑人生按文中的排查清单从第4章的表格一行行过多半能解决实在不行就先把运行时当成设计器用把界面调起来再说。