🎮 打造优雅的应用程序架构
1. 🧩 命令系统基础
1.1 🤔 为什么需要命令?
在传统事件处理模型中,我们这样处理按钮点击:
private void Button_Click(object sender, RoutedEventArgs e)
{
// 处理点击逻辑
}
这种模式存在几个问题:
- 🚫 业务逻辑与UI代码混杂
- 🔌 难以实现启用/禁用状态管理
- 🔄 复用性差
命令系统解决了这些问题!
1.2 🏗️ ICommand接口
WPF命令系统基于ICommand接口:
public interface ICommand
{
event EventHandler CanExecuteChanged;
bool CanExecute(object parameter);
void Execute(object parameter);
}
1.3 🛠️ 实现基本命令
创建一个简单的命令类:
/// <summary>
/// 实现ICommand接口的RelayCommand类,用于将UI操作(如按钮点击)绑定到ViewModel中的方法
/// </summary>
public class RelayCommand : ICommand
{
// 私有字段,存储要执行的委托
private readonly Action _execute;
// 私有字段,存储判断命令是否可以执行的委托(可选)
private readonly Func<bool> _canExecute;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="execute">要执行的委托方法</param>
/// <param name="canExecute">判断命令是否可以执行的委托方法(可选)</param>
/// <exception cref="ArgumentNullException">当execute参数为null时抛出</exception>
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
// 检查execute参数是否为null,如果是则抛出异常
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
// 可选地设置canExecute委托
_canExecute = canExecute;
}
/// <summary>
/// 当命令的可执行状态可能已更改时触发的事件
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// 确定此命令是否可以在其当前状态下执行
/// </summary>
/// <param name="parameter">命令使用的数据(此实现中未使用)</param>
/// <returns>如果此命令可以执行,则为true;否则为false</returns>
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
/// <summary>
/// 执行命令
/// </summary>
/// <param name="parameter">命令使用的数据(此实现中未使用)</param>
public void Execute(object parameter) => _execute();
/// <summary>
/// 引发CanExecuteChanged事件,通知命令的可执行状态可能已更改
/// </summary>
public void RaiseCanExecuteChanged()
{
// 使用空事件参数触发CanExecuteChanged事件
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
2. 🏛️ MVVM模式详解
2.1 🧱 MVVM三大组件
组件 | 职责 | 示例 |
---|---|---|
🎨 View | 用户界面展示 | MainWindow.xaml |
📦 ViewModel | 业务逻辑和状态 | MainViewModel.cs |
🗃️ Model | 数据模型和业务规则 | Product.cs |
2.2 🏗️ 创建ViewModel基类
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
2.3 🎯 典型ViewModel示例
public class ProductViewModel : ViewModelBase
{
private string _searchText;
private ObservableCollection<Product> _products;
public string SearchText
{
get => _searchText;
set
{
if (SetField(ref _searchText, value))
{
SearchCommand.RaiseCanExecuteChanged();
}
}
}
public ObservableCollection<Prod