一个 Blazor/WinForm 开发者的 WPF 学习记:通往 Avalonia 的那条路

📅 2026/7/25 16:11:23
一个 Blazor/WinForm 开发者的 WPF 学习记:通往 Avalonia 的那条路
一个 Blazor/WinForm 开发者的 WPF 学习记通往 Avalonia 的那条路作为一名长期深耕 Blazor 和 WinForm 的开发者我对“UI 框架”的理解一直停留在两个极端Blazor 的声明式组件模型和 WinForm 的拖拽式事件驱动。直到我开始接触 WPF并最终被 Avalonia 的跨平台魅力所吸引我才发现——原来 UI 开发可以如此灵活又如此“抽象”。这篇文章我将从实战出发记录我作为 Blazor/WinForm 开发者学习 WPF 并走向 Avalonia 的心路历程与核心代码示例。## 从 WinForm 到 WPF数据绑定与 XAML 的第一次碰撞WinForm 的编程模型非常直观你拖一个按钮双击它写事件处理代码。但 WPF 告诉我“别急先学 XAML 和绑定。” 第一次看到 XAML 标签时我的 Blazor 血统让我觉得亲切——毕竟 Blazor 的 Razor 语法也是基于模板的。但 WPF 的DataContext和Binding让我重新理解了“视图与逻辑分离”。### 代码示例 1WPF 中的数据绑定与命令模式下面是一个简单的 WPF 登录窗口展示了如何使用INotifyPropertyChanged实现数据双向绑定以及如何使用ICommand处理按钮点击xml!-- MainWindow.xaml --Window x:ClassWpfDemo.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml TitleWPF 登录 Height200 Width300 StackPanel Margin10 Label Content用户名 / TextBox Text{Binding UserName, UpdateSourceTriggerPropertyChanged} / Label Content密码 / PasswordBox x:NamepasswordBox / Button Content登录 Command{Binding LoginCommand} CommandParameter{Binding ElementNamepasswordBox, PathPassword} / TextBlock Text{Binding Status} ForegroundRed / /StackPanel/Windowcsharp// MainWindow.xaml.cs 与 ViewModelusing System.ComponentModel;using System.Windows.Input;public class LoginViewModel : INotifyPropertyChanged{ private string _userName; private string _status; public string UserName { get _userName; set { _userName value; OnPropertyChanged(nameof(UserName)); } } public string Status { get _status; set { _status value; OnPropertyChanged(nameof(Status)); } } public ICommand LoginCommand { get; } public LoginViewModel() { LoginCommand new RelayCommandstring(OnLogin); } private void OnLogin(string password) { // 模拟登录验证 if (UserName admin password 123456) Status 登录成功; else Status 用户名或密码错误。; } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propName) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));}// 简单 RelayCommand 实现public class RelayCommandT : ICommand{ private readonly ActionT _execute; public RelayCommand(ActionT execute) _execute execute; public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) true; public void Execute(object parameter) _execute((T)parameter);}学习感悟WPF 的绑定机制让我从“直接操作控件属性”转向了“操作 ViewModel”。这种模式在 Blazor 中也有类似体现比如bind-Value但 WPF 的ICommand和INotifyPropertyChanged更底层也更灵活。## 从 WPF 到 Avalonia跨平台的“同一套逻辑”当我学会了 WPF 的 MVVM 模式后Avalonia 的出现就像是一个“惊喜升级”。Avalonia 几乎完全兼容 WPF 的 XAML 语法和绑定机制但最大的优势是跨平台Windows、macOS、Linux甚至 Web通过 WebAssembly。对于我这种习惯了 Blazor 的 Web 思维、又留恋 WPF 的桌面体验的开发者来说Avalonia 简直是天作之合。### 代码示例 2Avalonia 中的响应式 UI 与样式系统以下是一个 Avalonia 的简单计算器界面展示了如何使用ReactiveUIAvalonia 推荐的 MVVM 框架和Styles实现主题化xml!-- MainWindow.axaml --Window xmlnshttps://github.com/avaloniaui xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:vmusing:AvaloniaCalc.ViewModels x:ClassAvaloniaCalc.Views.MainWindow TitleAvalonia 计算器 Width300 Height400 Window.Styles Style SelectorButton Setter PropertyBackground Value#FF4500 / Setter PropertyForeground ValueWhite / Setter PropertyFontSize Value18 / Setter PropertyMargin Value4 / /Style /Window.Styles Grid RowDefinitionsAuto,*,Auto Margin10 !-- 显示区域 -- TextBox Grid.Row0 Text{Binding Display} IsReadOnlyTrue FontSize28 HorizontalAlignmentRight Margin0,0,0,10 / !-- 按钮网格 -- Grid Grid.Row1 ColumnDefinitions*,*,*,* RowDefinitions*,*,*,* Button Content7 Grid.Column0 Grid.Row0 Command{Binding InputCommand} CommandParameter7 / Button Content8 Grid.Column1 Grid.Row0 Command{Binding InputCommand} CommandParameter8 / Button Content9 Grid.Column2 Grid.Row0 Command{Binding InputCommand} CommandParameter9 / Button Content/ Grid.Column3 Grid.Row0 Command{Binding InputCommand} CommandParameter/ / !-- 其他按钮类似省略部分 -- /Grid !-- 等号按钮 -- Button Grid.Row2 Content Command{Binding CalculateCommand} Height50 Margin4 / /Grid/Windowcsharp// MainWindowViewModel.csusing ReactiveUI;using System.Reactive;public class MainWindowViewModel : ReactiveObject{ private string _display 0; private string _currentInput ; private string _operator ; private double _firstOperand; public string Display { get _display; set this.RaiseAndSetIfChanged(ref _display, value); } // 使用 ReactiveUI 的 ReactiveCommand public ReactiveCommandstring, Unit InputCommand { get; } public ReactiveCommandUnit, Unit CalculateCommand { get; } public MainWindowViewModel() { InputCommand ReactiveCommand.Createstring(input { // 处理数字和运算符输入 if (double.TryParse(input, out _)) { _currentInput input; Display _currentInput; } else // 运算符 { _firstOperand double.Parse(_currentInput); _operator input; _currentInput ; } }); CalculateCommand ReactiveCommand.Create(() { var secondOperand double.Parse(_currentInput); double result _operator switch { _firstOperand secondOperand, - _firstOperand - secondOperand, * _firstOperand * secondOperand, / _firstOperand / secondOperand, _ 0 }; Display result.ToString(); _currentInput result.ToString(); }); }}学习感悟Avalonia 的ReactiveUI比 WPF 的INotifyPropertyChanged更简洁RaiseAndSetIfChanged自动处理属性变更通知。而且Avalonia 的样式系统可以全局覆盖这让我想起了 CSS但更贴近 XAML 的直观语法。## 我的迁移路线图从 Blazor 到 Avalonia 的桥梁作为 Blazor 开发者我最喜欢的是组件化思想code块、bind绑定。而 WPF 的UserControl和DataTemplate让我意识到Avalonia 的UserControl和ReactiveUserControl几乎可以无缝替代 Blazor 的组件。所以我的学习路线是1.先掌握 WPF 的 MVVM 核心绑定、命令、DataContext。2.然后直接跳到 Avalonia因为语法 90% 相同只是命名空间和某些控件名不同比如TextBox的Text绑定完全一致。3.最后用 Avalonia 的ReactiveUI替代 WPF 的手写INotifyPropertyChanged提升开发效率。## 总结从 WinForm 的“拖拽事件”到 Blazor 的“声明式组件”再到 WPF 的“MVVM 数据绑定”最终落脚于 Avalonia 的“跨平台 MVVM”——这条路并不平坦但每一次跃迁都让我对 UI 开发有了更深的理解。对于同样背景的开发者我的建议是不要被 WPF 的复杂度吓倒直接从 Avalonia 开始学因为它的社区活跃、文档完善而且能让你用一套代码同时覆盖桌面和未来可能的 Web 端。如果你已经熟悉 Blazor 的绑定和组件那么 Avalonia 的ReactiveUI会让你找到“家”的感觉。