WPF命令

命令是一种设计模式(命令模式,Command Pattern),用于将"请求"封装为一个对象,从而:

解耦调用者(如按钮)与执行者(如 ViewModel 中的方法)

支持统一的启用/禁用控制(CanExecute)

实现撤销(Undo)、日志、队列等高级功能

在 WPF 中,命令通过 ICommand 接口实现。

csharp 复制代码
public interface ICommand
{
    event EventHandler CanExecuteChanged;
    bool CanExecute(object parameter);
    void Execute(object parameter);
}

Execute:执行命令时调用的方法(相当于"点击后做什么")

CanExecute:决定命令是否可用(返回 true/false,影响按钮是否可点击)

CanExecuteChanged:当命令的可用状态可能改变时触发,通知 UI 刷新(如按钮自动变灰)

🌟 一、先打个比方:命令就像"遥控器"

想象你有一个电视(代表界面上的按钮),还有一个遥控器(代表命令)。

  • 你按遥控器上的"开机"键(相当于点击按钮),
  • 遥控器会把"开机"这个指令发给电视,
  • 电视收到后就执行"打开屏幕"。

在这个过程中:

  • 你不需要知道电视内部怎么工作的(解耦),
  • 如果电视没插电,遥控器会自动变灰,按不了(自动禁用)。

👉 WPF 的命令就是这个"遥控器":它把"用户操作"和"实际要做的事"分开,让代码更清晰、更好维护。


🧱 二、命令的核心:ICommand 接口

在 WPF 中,所有命令都要实现一个叫 ICommand 的接口。它只有两个关键方法:

方法 作用 类比
Execute() 真正要做的事情(比如保存文件) 按下遥控器,电视开机
CanExecute() 判断现在能不能执行(比如没填内容就不能保存) 电视没电了,遥控器自动锁住

💡 还有一个事件 CanExecuteChanged,用来告诉界面:"我的可用状态变了,请刷新按钮颜色!"


🛠️ 三、自己做一个简单的命令(RelayCommand)

.NET 没有直接提供现成的命令类,所以我们通常自己写一个通用的命令类,叫 RelayCommand

第一步:创建 RelayCommand

csharp 复制代码
using System;
using System.Windows.Input;

public class RelayCommand : ICommand
{
    private readonly Action _execute;           // 要执行的方法
    private readonly Func<bool> _canExecute;    // 判断能不能执行

    public RelayCommand(Action execute, Func<bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    // 谁想知道我能不能用?我状态变了会通知你!
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    // 能不能执行?
    public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;

    // 执行!
    public void Execute(object parameter) => _execute();
}

✅ 这个类你可以复制到任何 WPF 项目里复用!


🏗️ 四、在 ViewModel 中使用命令(MVVM 模式)

假设我们做一个"留言本":输入文字,点"保存"按钮。

1. 创建 ViewModel

csharp 复制代码
public class MainViewModel
{
    // 用户输入的内容
    public string Message { get; set; }

    // 保存命令
    public ICommand SaveCommand { get; }

    // 构造函数
    public MainViewModel()
    {
        SaveCommand = new RelayCommand(
            execute: OnSave,          // 点击时做什么
            canExecute: CanSave       // 什么时候能点
        );
    }

    // 真正的保存逻辑
    private void OnSave()
    {
        MessageBox.Show($"保存成功:{Message}");
    }

    // 判断:只有输入了内容才能保存
    private bool CanSave()
    {
        return !string.IsNullOrWhiteSpace(Message);
    }
}

🖼️ 五、在 XAML 中绑定命令

xml 复制代码
<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="留言本" Width="300" Height="150">
    
    <StackPanel Margin="10">
        <!-- 输入框,绑定到 Message -->
        <TextBox Text="{Binding Message, UpdateSourceTrigger=PropertyChanged}" 
                 Margin="0,0,0,10" />

        <!-- 按钮,绑定到 SaveCommand -->
        <Button Content="保存" 
                Command="{Binding SaveCommand}" 
                Height="30" />
    </StackPanel>
</Window>

关键点解释:

  • Text="{Binding Message, UpdateSourceTrigger=PropertyChanged}"
    → 用户一打字,Message 属性立刻更新。
  • Command="{Binding SaveCommand}"
    → 按钮自动监听命令的 CanExecute
    • 如果 CanSave() 返回 false(比如空内容),按钮自动变灰、不可点!
    • 如果返回 true,按钮可点击,点击就执行 OnSave()

完全不用写后台代码(.xaml.cs)!


🔁 六、命令 vs 传统事件(对比)

方式 传统事件 命令(Command)
代码位置 写在 .xaml.cs(Code-Behind) 写在 ViewModel(逻辑层)
按钮禁用 手动写 button.IsEnabled = false; 自动根据 CanExecute 控制
可测试性 难(依赖 UI) 容易(纯 C# 类,可单元测试)
复用性 高(同一个命令可用于按钮、菜单、快捷键)

🎯 命令让界面和逻辑彻底分离,是 MVVM 的核心!


🎁 七、额外小技巧

1. 给命令传参数

比如删除某条记录,需要传 ID:

csharp 复制代码
// 命令定义
public ICommand DeleteCommand { get; }

DeleteCommand = new RelayCommand<string>(id => DeleteItem(id));

// XAML
<Button Content="删除" 
        Command="{Binding DeleteCommand}" 
        CommandParameter="123" />

2. 快捷键也能用命令!

xml 复制代码
<Window.InputBindings>
    <KeyBinding Key="S" Modifiers="Ctrl" Command="{Binding SaveCommand}" />
</Window.InputBindings>

→ 按 Ctrl+S 就能保存,和按钮共用同一个命令!


✅ 总结:一句话记住命令

命令 = 把"按钮点击"变成"可绑定、可控制、可复用"的逻辑对象。

它让你:

  • 不用写 .xaml.cs 代码
  • 按钮自动变灰/变亮
  • 逻辑集中、易于测试
  • 支持快捷键、菜单、按钮统一处理

相关推荐
Chris _data7 天前
WPF 学习第三天 — Modbus RTU 串口通信
hadoop·学习·wpf
布吉岛的石头7 天前
Java 程序员第 43 阶段05:微服务整合大模型,跨服务调用架构设计实战,Seata分布式事务实战
wpf
步步为营DotNet7 天前
基于.NET Aspire 实现云原生应用的高效监控与可观测性
云原生·.net·wpf
芒鸽8 天前
HarmonyOS 分布式开发实战:设备协同、数据共享与跨设备迁移
分布式·wpf·harmonyos
Volunteer Technology8 天前
Flink状态管理与容错(二)
大数据·flink·wpf
happyprince9 天前
07_verl-Trainer模块详解
人工智能·架构·wpf·强化学习
bugcome_com9 天前
WPF + Prism 技术指南与实战项目(二、模板搭建)
wpf
小满Autumn9 天前
log4net 日志框架 — 从配置到实战速查手册
笔记·c#·.net·wpf·上位机·log4net
政沅同学9 天前
基于 C# WPF + HALCON 的工业视觉算法工具框架(开源)
开发语言·c#·wpf
happyprince10 天前
03_verl-设计理念与核心原理
wpf