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 代码
  • 按钮自动变灰/变亮
  • 逻辑集中、易于测试
  • 支持快捷键、菜单、按钮统一处理

相关推荐
玖笙&7 小时前
✨WPF编程进阶【7.3】集成动画(附源码)
c++·c#·wpf·visual studio
ifeng091814 小时前
鸿蒙分布式调试挑战:跨设备数据流转与连接稳定性
分布式·wpf·harmonyos
Macbethad21 小时前
如何使用WPF做工控主页
wpf
Aevget1 天前
DevExpress WPF中文教程:Data Grid - Service(服务)示例
wpf·界面控件·devexpress·ui开发
Macbethad1 天前
WPF工控软件的设计方案
wpf
Macbethad3 天前
使用WPF编写一个读取串口的程序
wpf
Macbethad5 天前
如何用WPF做工控设置界面
java·开发语言·wpf
csdn_aspnet5 天前
WPF 做一个简单的电子签名板(一)
c#·wpf
玖笙&5 天前
✨WPF编程进阶【7.2】:动画类型(附源码)
c++·c#·wpf·visual studio