Avalonia 与 WPF 对比

总结 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> 不支持内联,必须使用 ColumnDefinitionRowDefinition 定义
容器控件设置子项间距 <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 内置的主题 SimpleThemeFluentTheme 无需额外代码即可无缝支持 DarkLight 变体

如果要覆盖全局应用程序变体,设置 ApplicationRequestedThemeVariant 属性:

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. 演示项目#

Avalonia UI 演示

❤️ 如果这篇文章对你有帮助,欢迎赞助支持我继续维护 ❤️

☕ Support me ⚡ 爱发电赞助