总结 Avalonia 与 WPF 差异,以及一个完整的、支持 AOT 的 Avalonia 程序开发要点
1. 差异#
| 内容 | Avalonia | WPF |
|---|---|---|
| Xaml 文件 | .axaml |
.xaml |
预览的 DataContext |
<Design.DataContext><vm:MainWindowViewModel/></Design.DataContext> |
d:DataContext="{d:DesignInstance Type=vm:MainWindowViewModel}" |
Grid |
<Grid ColumnDefinitions="120,100" RowDefinitions="Auto,Auto,Auto"></Grid> |
不支持内联,必须使用 ColumnDefinition 和 RowDefinition 定义 |
| 容器控件设置子项间距 | <Grid RowSpacing="10" ColumnSpacing="10"></Grid><StackPanel Spacing="10"></StackPanel><DockPanel VerticalSpacing="10" HorizontalSpacing="10"></DockPanel> |
不支持,通常通过设置子项的 Margin 实现同样效果 |
| 绑定 | 分为编译绑定( CompiledBinding )和反射绑定( ReflectionBinding ) |
反射绑定 |
| 样式 | 类似CSS,存储在一个独立的 Styles 集合中 <Style Selector="XXXControl.xxxStyle"></Style><XXXControl Classes="xxxStyle"></XXXControl> |
存储在 Resources 集合中 |
| 数据模板 | 数据模板只能放置在控件的 DataTemplates 集合内,或者放置在 Application 上 |
存储在 Resources 集合中 |
2. 自定义窗口标题栏#
在 Avalonia 中设置窗口的以下属性
ExtendClientAreaChromeHints="PreferSystemChrome"
ExtendClientAreaTitleBarHeightHint="30"
ExtendClientAreaToDecorationsHint="True"然后在根据设置的标题栏高度自定义一个标题栏即可
需要注意:
- 将自定义标题栏的图标与窗口图标同步,确保用户体验
- 实现自定义标题栏拖动以及双击效果(最大化和还原)
3. 语言切换#
Avalonia 官方文档提供了静态语言切换的方案,即 x:Static + .resx ,这种方式当切换语言时,界面上的字符串资源需要重启软件才能生效,新创建的 UI 元素会立即生效
要想支持动态切换,需要通过绑定的方式,这里有两种实现思路:
- 在 ViewModel 中实现,在语言切换时触发所有字符串资源属性的变更事件
- 在 Xaml 中实现,需要自定义标记扩展,使用方便
4. 主题切换#
Avalonia 内置的主题 SimpleTheme 和 FluentTheme 无需额外代码即可无缝支持 Dark 和 Light 变体
如果要覆盖全局应用程序变体,设置 Application 的 RequestedThemeVariant 属性:
application.RequestedThemeVariant = ThemeVariant.Dark;application.RequestedThemeVariant = ThemeVariant.Light;application.RequestedThemeVariant = ThemeVariant.Default;5. AOT 发布#
Avalonia 最吸引我的就是跨平台自绘和支持 AOT 这两点了
跨平台自绘保证了不同平台 UI 的一致性
AOT 则使应用启动更快,不依赖目标 .NET 运行时版本,结合修剪可以减小发布物尺寸
我写的一个测试应用,支持语言切换、主题切换、包含 MaterialDesign Icons、支持与 C/C++ dll 互操作、NLog日志、Json配置文件、加解密算法等,发布后大小在 38MB(其中 Avalonia 运行时依赖占用 14MB),压缩后仅 14MB
AOT 发布后的应用启动速度有相当明显的提升,基本“秒开”,比 WPF 启动快,比带着 MEF 的 WPF 启动快多了(即使是使用了 VisualStudio 优化的 MEF 组件),
最后一个优点就是代码不那么容易被反编译了
6. 如何支持 AOT#
Avalonia 官方文档给出了一些建议
另外:
- 对于运行前已确定的需要运行时反射生成的代码,改为使用 T4 模板或源生成器,
- 样板代码尽量使用代码生成技术,使用辅助工具生成代码
- 依赖 AOT 兼容的库,自己的库同样要做 AOT 兼容性分析
7. 基于条件编译的版本切换#
当想发布一个测试版本的 UI 时,可以通过条件编译实现一套代码不同 UI ,而且结合裁剪,发布的版本不会包含其他版本的代码
7.1 在代码中使用条件编译#
对于后台代码,使用预编译指令即可实现,如:通过 #if BETA,下面介绍如何在 XAML 中实现
7.2 在 XAML 中使用条件编译#
添加命名空间
xmlns:mark="using:CommonUtility.Avalonia.MarkupExtensions"示例代码
<ContentControl>
<ContentControl.Content>
<mark:BuildConfig>
<mark:BuildConfig.Beta>
<TextBlock Text="Show In Beta"/>
</mark:BuildConfig.Beta>
<mark:BuildConfig.Stable>
<TextBlock Text="Show In Stable"/>
</mark:BuildConfig.Stable>
<mark:BuildConfig.Default>
<TextBlock Text="Default"/>
</mark:BuildConfig.Default>
</mark:BuildConfig>
</ContentControl.Content>
</ContentControl>这样在编译时通过以下命令发布测试版本
dotnet publish -c Release -p:DefineConstants="BETA"8. 演示项目#
