基于avalonia的上位机软件开发(WPF迁移)
之前我使用 WPF 和 WinForms 开发过上位机软件之前的文章这两种 UI 框架在业界应用广泛拥有丰富的开发经验和参考资料。不过它们并不支持跨平台。如今 .NET 已经开源并实现了跨平台能力而 Avalonia 正是一款跨平台 UI 框架因此我决定尝试一下 Avalonia。目前相关的代码仓库位于gitee点此与github点此。编译后 的软件在win11下可正常运行其他平台还没测试。当然选择 Avalonia 并非一时兴起而是经过了一番考量。首先Avalonia 的 API 设计与 WPF 高度相似都基于 XAML 声明式 UI 和 MVVM 模式这意味着我过去积累的 WPF 开发经验可以平滑迁移学习成本相对较低。其次Avalonia 支持 Windows、macOS、Linux 等多个桌面平台未来如果项目需要部署到非 Windows 环境无需重写界面层。此外AI的发展让编程开发的难度减小了不少可以帮助你快速将WPF的代码迁移到Avalonia。目前上位机的效果开发环境和WPF一样用Visual Studio可以进行开发当然也可以用CLion不过我习惯了用VS开发。编程前装上Avalonia for Visual Studio扩展这样打开VS建立工程的时候就有现成模板可以选择。不过最近更新了扩展之后预览器有时候会失效当然代码还是能正常跑的就是调试起来麻烦了。语法差异根命名空间WPF 使用xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationAvalonia 使用xmlnshttps://github.com/avaloniauix:Static 静态资源WPF 支持在 XAML 中通过 x:Static 引用静态属性Avalonia 中此用法在 UserControl 的 DataContext 绑定上不可靠需在构造函数代码中赋值DataContext AppViewModel.Instance;控件 PaddingAvalonia 的 StackPanel 没有 Padding 属性。需用 Border 包裹替代BorderPadding10,0,10,0StackPanel.../StackPanel/BorderTextBox 滚动条WPF 可以将 ScrollViewer 嵌套在 TextBox 外部实现滚动。Avalonia 中 TextBox 的 ScrollViewer.VerticalScrollBarVisibility 会报错AVLN2000直接使用 TextBox 原生的多行滚动能力或依赖系统默认的滚动行为。ComboBox DataTemplateWPF ComboBox 支持 DisplayMemberPath / DisplayMemberBinding 直接指定显示字段。Avalonia 的 ComboBox 不支持这两个属性必须显式定义 ItemTemplateComboBoxItemsSource{Binding PathCanBaudList}ComboBox.ItemTemplateDataTemplateTextBlockText{Binding PathText}//DataTemplate/ComboBox.ItemTemplate/ComboBox构造函数要求Avalonia 中 XAML 定义的控件类必须具有公开无参构造函数否则在 XAML 实例化时抛出 AVLN3001 错误。即使使用构造函数注入 ViewModel也必须提供公开无参构造publicSerialConfigWindow(){InitializeComponent();}publicSerialConfigWindow(CanConfigViewModelvm){InitializeComponent();_vmvm;DataContext_vm;}控件行为差异TextBox.ScrollToEndWPF 的 TextBox 支持 ScrollToEnd() 方法将视图滚动到底部。Avalonia 中该方法不存在替代方案是将 CaretIndex 设置为 Text.LengthtextBox.CaretIndex textBox.Text.Length;DataGrid 数据源WPF DataGrid 可以直接绑定 DataTable.DefaultView、ObservableCollection、ExpandoObject 等动态数据源。Avalonia DataGrid 对这些动态数据源支持有限可能不显示内容或抛异常。迁移经验如果 DataGrid 绑定复杂数据源失败可改用 ListBox StackPanel 方案表头用固定 StackPanel Border 定义每个数据行用 ListBox.ItemTemplate 中的 StackPanel Border 表示通过 _showCellMap: Dictionaryint, Dictionaryint, TextBlock 维护单元格引用运行时直接更新 TextBlock.Text 实现动态数据刷新DispatcherTimer.Dispose()WPF 的 DispatcherTimer 实现了 IDisposable 接口可以调用 Dispose()。Avalonia 的 DispatcherTimer 没有 Dispose() 方法仅需要调用 Stop() 并将引用置 null_timer.Stop();_timer null;FileDialog 默认文件名WPF SaveFileDialog.FileName 可直接设置默认文件名。Avalonia 的 SaveFileDialog 中 FilePickerSaveOptions.DefaultFileName 属性不存在需使用 SaveFileDialog.InitialFileNamevar dialog new SaveFileDialog();dialog.InitialFileName “config.csv”;颜色和字体差异颜色创建WPF 支持Color.FromHex(“#FFFFFF”)Color.FromRgb(255, 255, 255)Avalonia 不支持 Color.FromHex / ParseHex必须用构造函数new Color(0xFF, 255, 255, 255)Avalonia 的 Color 构造函数签名Color(byte alpha, byte r, byte g, byte b)alpha 参数不可省略。FontWeights.BoldAvalonia 中 FontWeights.Bold 在某些上下文中无法直接引用。简单的加粗需求可在 XAML 中使用 FontWeight“Bold” 或直接移除粗体设置因为 Avalonia Fluent 主题已自带良好的视觉层次。数据绑定关键差异PropertyChanged 通知Avalonia 的 MVVM 属性变更通知机制与 WPF 类似都基于 INotifyPropertyChanged。但在 Avalonia 松散绑定模式下UI 更新更严格依赖 OnPropertyChanged 触发错误示例字段直接赋值UI 不更新_selectedCanBaud value;// → 不调用 setter不触发 OnPropertyChangedUI 无变化正确示例通过属性 setter 赋值SelectedCanBaud value;// → 调用 setter → SetProperty() → OnPropertyChanged → UI 更新绑定语法Avalonia 松散绑定下支持 WPF 风格的 Path 语法但部分复杂绑定可能需要调整使用 Path 前缀显式声明绑定路径避免使用复杂的绑定转换器Converter改用代码中的逻辑处理SelectedIndex 绑定需要确保 ComboBox.Items 已经初始化完毕绑定到集合Avalonia 支持 ObservableCollection 作为绑定数据源。但与 WPF 不同ComboBox 绑定 ObservableCollection 时如果列表在运行时被替换而非逐个 Add/RemoveSelectedIndex 可能无法正确更新对于 ListBox 绑定复杂动态数据推荐使用 DataTemplate 代码维护单元格引用线程模型差异跨线程 UI 更新WPF 中跨线程更新 UI 使用 Dispatcher.Invoke()Dispatcher.Invoke((){SomeTextBlock.Textresult;});Avalonia 中使用 Dispatcher.UIThread 或自定义辅助方法Dispatcher.UIThread.Invoke((){SomeTextBlock.Textresult;});也可以创建 UiDispatcherHelper 封装跨平台调用内部通过 #if WINDOWS 判断使用Dispatcher.UIThread 或 Dispatcher.CurrentDispatcher。串口 DataReceived 事件WPF 的 SerialPort.DataReceived 事件在后台线程触发通过 Dispatcher.Invoke 更新 UI。Avalonia 中同样需要跨线程调度推荐使用 TaskCompletionSource 模式在 DataReceived 事件中仅积累数据到 StringBuilder 缓冲区通过 TaskCompletionSource 在主线程 await 结果使用 lock 保护共享缓冲区避免并发访问问题实际迁移经验Avalonia 的 DataReceived 事件触发时机和线程行为与 WPF 基本一致但 TaskCompletionSource 的超时处理和缓冲区的清理逻辑需要仔细测试。文件操作差异文件对话框WPFvardialognewSaveFileDialog();dialog.DefaultExt.csv;dialog.FileNamedata.csv;if(dialog.ShowDialog()true)File.WriteAllText(dialog.FileName,content);AvaloniavarparentTopLevel.GetTopLevel(this);vardialognewSaveFileDialog();dialog.InitialFileNamedata.csv;dialog.Filtersnew[]{newFileDialogFilter(){NameCSV,Extensionsnew[]{csv}}};varresultawaitdialog.ShowAsync(parent);if(result!null)File.WriteAllText(result,content);关键差异Avalonia 的 SaveFileDialog 使用异步 ShowAsync()返回 Taskstring?。不支持 IStorageFile 模式FilePickerSaveOptions.DefaultFileName 不存在回退到 InitialFileName 文件路径字符串的方式。编码处理WPF 支持 Encoding.GetEncoding(“GB2312”) 等任意编码。Avalonia 在某些环境下 GB2312 编码不可用需要回退到 Encoding.Default系统默认编码在中文 Windows 下等价于 GB2312var encoding Encoding.Default; // 替代 Encoding.GetEncoding(“GB2312”)对话框消息框WPF 使用 MessageBoxMessageBox.Show(“内容”, “标题”)Avalonia 没有内置 MessageBox需要创建 DialogHelper 封装await DialogHelper.ShowAsync(“内容”, “标题”);内部通过 TopLevel.GetTopLevel(control).ShowDialog 显示一个简单 Window。自定义窗口Avalonia 的自定义窗口必须通过 TopLevel.GetTopLevel 获取父窗口引用才能正确显示var parent TopLevel.GetTopLevel(this);var dialog new SerialConfigWindow(vm);dialog.ShowDialog(parent);与 WPF 的 ShowDialog(owner) 不同Avalonia 的 ShowDialog 需要传入 Window 参数非 Window 也接受。Avalonia 迁移WPF的总结▶ 属性变更必须走 setter任何时候更新绑定属性都必须通过属性 setterSetProperty不要直接赋值私有字段否则 UI 不会更新。▶ ComboBox 必须用 DataTemplate不要使用 DisplayMemberPath/DisplayMemberBinding不支持改用 ItemTemplate 内嵌 TextBlock。▶ 使用 Border 替代 StackPanel.PaddingAvalonia StackPanel 无 Padding 属性用 Border 包裹实现间距效果。▶ 对话框异步化SaveFileDialog 使用 ShowAsync()MessageBox 需要自定义 DialogHelper。▶ DataGrid 复杂数据用 ListBoxDataGrid 对 DataTable/ExpandoObject 等动态数据源支持有限复杂数据用 ListBox DataTemplate 代码维护单元格引用。▶ 编码统一用 Encoding.Default避免使用 Encoding.GetEncoding(“GB2312”)直接用 Encoding.Default。▶ 跨线程更新用 Dispatcher.UIThread替代 WPF 的 Dispatcher.Invoke或通过 UiDispatcherHelper 封装。▶ 文件对话框用 InitialFileName替代 WPF 的 DefaultExt/FileName注意异步调用模式。▶ 颜色用 Color(0xFF, R, G, B) 构造函数不要用 Color.FromHex不存在也不要用 Color.FromArgb。

相关新闻

从触摸屏到E-Ink刷新:用C语言实现Kindle手指书写Fingerink

从触摸屏到E-Ink刷新:用C语言实现Kindle手指书写Fingerink

旧款 Kindle Paperwhite 被闲置,往往不是因为屏幕坏了,而是因为系统封闭、应用太少。社区里出现了一个叫 Fingerink 的项目,目标是让用户在 Kindle Paperwhite 上直接用手指书写。这个想法听起来不大,真正拆开后却发现是一整套底层…

2026/11/5 5:37:10 阅读更多 →
#112_东山笔记_关于FreeRTOS移植树文件的内容说明

#112_东山笔记_关于FreeRTOS移植树文件的内容说明

推荐FreeRTOS教程 链接一:FreeRTOS官网 链接二:百问网FreeRTOS教程 链接三:百问网移至文件 1. 核心源文件(Source 根目录) 这些文件是 FreeRTOS 内核的通用实现,与具体硬件无关,因此放在 Source 根目录下,所有移植版…

2026/10/17 5:23:29 阅读更多 →
Photo to Video AI + Motion Prompt:让静态照片动起来的关键技巧

Photo to Video AI + Motion Prompt:让静态照片动起来的关键技巧

先想一个场景。你手里有一张人物照片,想让它微笑、眨眼、转过头来。放在五年前,这意味着你需要学一套动画管线:先抠图、再建模、绑定骨骼、打关键帧、调补间,最后还要处理脸部纹理在运动时崩坏的问题。放在现在,这张照…

2026/10/17 5:23:35 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/4 1:35:24 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/5 1:08:50 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/3 11:35:49 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/4 6:23:50 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/5 0:54:28 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/4 19:19:54 阅读更多 →

日新闻

周新闻

月新闻