一、为什么我们需要命令绑定进阶

在日常开发中,WPF的命令绑定机制帮我们实现了UI和业务逻辑的解耦。但面对复杂的业务场景时,简单的ICommand实现往往捉襟见肘。比如:

  • 需要跨多个控件传递命令参数
  • 动态切换命令执行逻辑
  • 处理多层嵌套的MVVM架构

这时候,基础的RelayCommand就显得力不从心了。

二、命令传递的典型问题场景

假设我们有一个订单管理系统(技术栈:WPF + .NET 6),需要实现这样的逻辑:

  1. 主窗口的"保存"按钮需要根据子控件的验证状态决定是否可用
  2. 工具栏的"导出"操作需要聚合三个数据网格的选择项
// 基础命令实现(问题版本)
public class ProblematicCommand : ICommand
{
    public bool CanExecute(object parameter) => true; // 无法感知子控件状态
    public void Execute(object parameter) 
    {
        // 无法获取跨控件数据
    }
    public event EventHandler CanExecuteChanged;
}

三、进阶解决方案:命令路由体系

通过构建命令路由系统实现跨组件通信:

3.1 建立命令中枢

public class CommandRouter
{
    private static readonly Dictionary<string, ICommand> _commands = new();
    
    // 注册全局命令
    public static void Register(string commandName, ICommand command) 
    {
        _commands[commandName] = command;
    }
    
    // 获取命令引用
    public static ICommand GetCommand(string commandName) 
    {
        return _commands.TryGetValue(commandName, out var cmd) ? cmd : null;
    }
}

3.2 实现可观察命令

public class ObservableCommand : ICommand
{
    private readonly Func<object, bool> _canExecute;
    private readonly Action<object> _execute;
    
    // 通过事件总线通知状态变化
    public event EventHandler CanExecuteChanged
    {
        add => CommandEventBus.Subscribe(value);
        remove => CommandEventBus.Unsubscribe(value);
    }
    
    public ObservableCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }
    
    public bool CanExecute(object parameter) => _canExecute?.Invoke(parameter) ?? true;
    
    public void Execute(object parameter) => _execute(parameter);
}

四、实战:复杂表单验证场景

实现一个需要联动验证的订单表单:

// 在ViewModel初始化时注册命令
public OrderViewModel()
{
    // 注册全局保存命令
    CommandRouter.Register("GlobalSave", new ObservableCommand(
        execute: _ => SaveAll(),
        canExecute: _ => _addressIsValid && _paymentIsValid
    ));
}

// 子控件验证状态变化时触发更新
private void OnAddressValidationChanged()
{
    _addressIsValid = CheckAddress();
    CommandEventBus.NotifyStateChanged(); // 通知所有订阅者
}

五、技术方案对比

方案 优点 缺点
传统RelayCommand 实现简单 无法处理跨组件通信
事件聚合器 完全解耦 需要额外维护事件订阅
命令路由系统 集中管理,灵活可控 需要设计初始架构

六、关键注意事项

  1. 内存管理:全局命令需要手动注销避免内存泄漏
  2. 线程安全:命令状态变更可能来自非UI线程
  3. 测试策略:建议使用Mock路由进行单元测试

七、总结

通过构建命令路由体系,我们实现了:

  • 跨组件命令状态同步
  • 动态命令逻辑替换能力
  • 可测试的松耦合架构

这种模式特别适合企业级应用开发,虽然前期需要更多设计工作,但在复杂业务场景下能显著提升代码可维护性。