WPF新手村教程(六)— 新手村BOSS战前准备(命令)
2026/7/7 9:17:16 网站建设 项目流程

一.概念

  • 我们先来看看命令这两个字,在汉语上的词意

    • 命令:一种权威性的指示,通常指上级对下级的口头或书面指示,要求其执行某项任务或采取某种行动
      命令往往具有明确的目标和要求,并带有一定的强制性
  • 然后我们再来看看WPF中命令的概念

    • 命令:代指一种明确的指令或者要求,用于向某个目标传递指定的操作或者行为
      • 因此,只从效果上来看,命令和事件的效果视乎一样,但是,命令 ≠ 事件
    • WPF中的命令是一种行为抽象机制,它将UI触发、执行逻辑和作用目标解耦,并通过CanExecute实现状态驱动的交互控制
      • 命令是对"行为"的对象化封装
      • 它将“触发者”、“执行逻辑”和“作用对象”解耦(前后端解耦),并提供“是否可执行”的状态控制机制(行为约束)
      • 命令(Command)= 把 "一个动作" 抽象成 一个对象
  • 在现实生活中,假设你在对一个AI发起了一个命令,那么我们的流程是

    • 编辑执行命令内容 -> 选择命令目标 -> 告诉AI执行什么操作 -> 行为约束 # 其中涉及到 命令发出者 命令接收者 命令内容 命令执行者 开始执行命令 完成执行内容 汇报执行结果
    • 而我们使用的命令,和这个流程其实非常相似(参考小结中的流程图)


二.命令的四个概念

  • 命令源(Command Source) ↓ # 触发 命令(Command) ↓ # 查找 命令绑定(Command Binding) ↓ # 执行 命令目标(Command Target)

1.0 命令(Command) —— 继承ICommand接口

  • 本质上其实就是实现了一个ICommand对象

  • 主要负责2件事情

    • Execute(执行逻辑)
    • CanExecute(能不能执行)

1.1 预定义命令库✳

  • 预定义命令库:WPF内置的一组标准命令(已经实现好的ICommand

    • 不用写RelayCommand了,开袋即食,别造轮子了
    • 预定义命令 = 官方帮你定义好的“行为规范”,而不是具体实现
  • 常用的命令库(预定义)有五个静态类

    • ApplicationCommands # 应用通用 NavigationCommands # 导航(页面跳转) EditingCommands # 文本编辑 ComponentCommands # 组件 MediaCommands # 媒体控制 # 使用时,请随用随查,而且除了第一个,基本上不怎么使用,了解即可 # 使用频率大概是: ApplicationCommands >>> EditingCommands > NavigationCommands > MediaCommands > ComponentCommands
  • 示例代码:

    • 新建一个操作,这个操作绑定到逻辑CommandBinding_Executed

    • <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed"/> </Window.CommandBindings>
  • 下面大致列举了一部分常用的预定义命令:

    • 随用随查,别背,没用,顶多记一下ApplicationCommands通用类的几个,毕竟大多数都用不上
(1)ApplicationCommands应用通用类
  • 命令作用常见场景
    Copy复制选中内容TextBox / 数据编辑
    Cut剪切选中内容文本编辑
    Paste粘贴剪贴板内容输入框
    Save保存数据文件/表单
    SaveAs另存为文件系统
    Open打开文件文件操作
    New新建内容编辑器
    Undo撤销操作编辑器
    Redo重做操作编辑器
    Delete删除选中项列表/文本
    Print打印内容报表/文档

(2)NavigationCommands导航(页面跳转)类
  • 命令作用常见场景
    Back返回上一页页面导航
    Forward前进页面导航
    Refresh刷新当前页面Web/数据页
    BrowseHome返回首页应用首页
    BrowseStop停止加载浏览器

(3)EditingCommands文本编辑类
  • 命令作用常见场景
    Delete删除内容文本编辑
    Backspace删除前一个字符输入框
    ToggleBold加粗文本富文本
    ToggleItalic斜体文本富文本
    IncreaseFontSize增大字体编辑器
    DecreaseFontSize减小字体编辑器

(4)ComponentCommands组件类
  • 命令作用常见场景
    MoveUp向上移动项列表
    MoveDown向下移动项列表
    MoveLeft向左移动UI操作
    MoveRight向右移动UI操作
    ExtendSelection扩展选区多选控件

(5)MediaCommands媒体控制类
  • 命令作用常见场景
    Play播放媒体视频/音频
    Pause暂停播放媒体控制
    Stop停止播放媒体控制
    Record开始录制录音
    NextTrack下一首音乐播放器
    PreviousTrack上一首音乐播放器
    VolumeUp音量增加媒体控制
    VolumeDown音量减少媒体控制

2.命令源 —— 继承ICommandSource

  • 调用命令的对象,通常是UI控件

    • 直白点说,就是,谁触发了命令,那个谁就是命令源

      • # 这里的命令源就是Button <Button Command="{Binding SaveCommand}" />
  • 命令源相关属性一般有3个,上面那个是最常用的,下面两个也了解一下

    • 第三个命令目标详细请见下一个小点

    • 属性作用备注
      🌱Command指定要触发的命令必须有,否则不会触发命令
      🌱CommandParameter传递给命令的参数可选,用于区分或传递数据
      🌱CommandTarget(命令目标)指定命令作用对象可选,不写默认作用于焦点控件(自身)

(1)Command
  • 作用指定触发哪个命令

  • 类型ICommand

  • 常用值ApplicationCommands.NewApplicationCommands.Copy等预定义命令,或者自定义RoutedCommand/RelayCommand

    • 点击按钮就触发 New 命令

    • <Button Command="ApplicationCommands.New" Content="新建"/>

(2)CommandParameter
  • 作用:给命令传递参数

  • 类型object

  • 常见场景

    • 同一个命令绑定到多个按钮,用参数区分不同操作
    • 传递数据给命令执行逻辑
  • 示例:

    • <Button Command="ApplicationCommands.Copy" CommandParameter="{Binding SelectedText}" Content="复制选中文本"/>
    • // 假设下面这个是我们绑定的逻辑Executed private void CopyCommand_Executed(object sender, ExecutedRoutedEventArgs e) { var text = e.Parameter; // 获取参数 CommandParameter MessageBox.Show($"复制内容: {text}"); }

3.命令目标CommandTarget

  • 在其目标上执行命令的对象,继承IInputElement即可

    • 一般情况下,命令源和命令目标是同一个
    • 直白点说就是,命令作用在谁身上,那个谁就是命令目标
      • 即:命令目标就是命令最终影响的对象
  • # 命令 = Copy # 命令源 = Button # 命令目标 = textBox <Button Command="ApplicationCommands.Copy" CommandTarget="{Binding ElementName=textBox}" />

4.命令绑定CommandBindings

  • 命令绑定:将某个命令和执行逻辑进行绑定

    • 命令的逻辑在哪里执行的?在哪里写的
  • # Executed:执行逻辑 => 函数OnCopy要自己写 # CanExecute:能不能执行 <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.Copy" Executed="OnCopy" CanExecute="OnCanCopy"/> </Window.CommandBindings>

三.自定义命令

  • 自定义命令有3种:RouteCommand(路由命令),RouteUICommand(路由命令UI增强版)和完全自定义命令
    • 我们了解一下即可,重点关注第三种
1.RouteCommand(路由命令)
  • 示例代码:点击按钮,弹出提示框

  • Test.xaml

    • <Window x:Class="Command_Demo.Test" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Command_Demo" xmlns:cmd="clr-namespace:Command_Demo.Command" mc:Ignorable="d" Title="Test" Height="450" Width="800"> <Window.CommandBindings> <CommandBinding Command="cmd:CustCmdByRouteCmd.Query" Executed="CommandBinding_Executed"/> </Window.CommandBindings> <Grid> <Button Height="100" Width="100" Command="cmd:CustCmdByRouteCmd.Query"/> </Grid> </Window>
  • Test.xaml.cs

    • namespace Command_Demo { /// <summary> /// Test.xaml 的交互逻辑 /// </summary> public partial class Test : Window { public Test() { InitializeComponent(); } private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("自定义命令执行完成!"); } } }
  • CustCmdByRouteCmd.cs

    • using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace Command_Demo.Command { /// <summary> /// 通过RouteCommand / RoutedUICommand 自定义命令 /// </summary> public class CustCmdByRouteCmd { private static RoutedCommand query; static CustCmdByRouteCmd() { query = new RoutedCommand("Query", typeof(CustCmdByRouteCmd)); } public static RoutedCommand Query { get { return query; } } } }

2.完全自定义命令1.0 —— 简易制作版
  • 示例代码:

  • ├─Command │ └─CustCmd.cs │ ├─Cust_Test.xaml └─Cust_Test.xaml.cs
    • Cust_Test.xaml

      • <Window x:Class="Command_Demo.Cust_Test" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Command_Demo" xmlns:cust="clr-namespace:Command_Demo.Command" mc:Ignorable="d" Title="Cust_Test" Height="450" Width="800"> <Window.Resources> <cust:CustCmd x:Key="custCmd"/> </Window.Resources> <Grid> <Button Width="150" Height="150" Content="我不是按钮" Command="{StaticResource custCmd}" CommandParameter="WWWWW"/> </Grid> </Window>
    • CustCmd.cs

      • using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; namespace Command_Demo.Command { /// <summary> /// 自定义命令 /// </summary> public class CustCmd : ICommand { /// <summary> /// 命令状态发生改变的事件(一般可能还用不上) /// </summary> public event EventHandler? CanExecuteChanged; /// <summary> /// 能不能执行(执行业务) /// </summary> /// <param name="parameter"></param> public bool CanExecute(object? parameter) { if (parameter == null) { // 如果参数为空,这个控件(按钮)你往死里点也不会发生什么事(给你ban了) return false; } return true; } /// <summary> /// 如何执行(命令状态) /// </summary> /// <param name="parameter"></param> public void Execute(object? parameter) { MessageBox.Show($"( •̀ ω •́ )ก็็็็็็็็็็็็็ {parameter}"); } } }

3.完全自定义命令2.0 —— 委托升级版
  • [!IMPORTANT]

    在此之前,我们来回顾一下委托的使用
    与其说是怎么讲自定义命令是怎么使用的,不如说是讲委托的实际运用

    既然你都学到MVVM了,那委托肯定也是学过了的,这边我们简单回顾一下即可
    如果不会,可以看我之前的随笔
    九成九新自用C#入门文档 - 假设狐狸有信箱 - 博客园

    • 委托类型用途示例
      Action执行操作,无返回值Action report = cal.Report;
      Func<T1, T2, TResult>
      T1,T2... : 参数1,参数2...
      Tesult:函数返回值
      执行操作,有返回值Func<int,int,int> add = cal.Add;
      delegate自定义委托类型delegate int Demo(int a, int b);
      Demo demo = cal.Add;
    • // 委托是如何使用的:声明 -> 实例化 -> 赋值 -> 调用 // 这里使用自定义委托类型delegate为例 using System; using static System.Console; namespace Program { // 1.声明 // 你可以在命名空间中定义一个类,接口或者委托 // 但是不能定义一个变量和函数 // 定义一个委托 delegate void Help(); // 定义一个类 public class Person { } public class Program { public static void Main() { // 2.实例 Help h; Person p; // 3.赋值 // 将一个函数赋值给一个委托实例 h = SayHello; // 4.调用 h(); h(); void SayHello() { WriteLine("哇哇哇哇"); } } } }

  • 命令示例代码:

    • ├─Command │ └─CustCmd_NoBusiness.cs │ ├─CustCmd_NotBus.xaml └─CustCmd_NotBus.xaml.cs
    • CustCmd_NotBus.xaml

      • <Window x:Class="Command_Demo.CustCmd_NotBus" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Command_Demo" xmlns:custCmd="clr-namespace:Command_Demo.Command" mc:Ignorable="d" Title="完全自定义命令-委托升级版" Height="450" Width="800"> <Window.Resources> <!--<custCmd:CustCmd_NoBusiness x:Key="custCmd" CmdAction="TestCmd"/>--> <custCmd:CustCmd_NoBusiness x:Key="custCmd" CmdFunc="TestCmd"/> </Window.Resources> <Grid> <Button Content="这是一个按钮" Height="150" Width="150" Command="{StaticResource custCmd}" CommandParameter="\/"/> </Grid> </Window>
    • CustCmd_NoBusiness.cs

      • using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; namespace Command_Demo.Command { /// <summary> /// 自定义命令:将业务从命令中分离 => 将处理业务函数传递给命令(系统委托) /// </summary> public class CustCmd_NoBusiness : ICommand { /// <summary> /// 命令状态发生改变的事件(一般可能还用不上) /// </summary> public event EventHandler? CanExecuteChanged; /* Action委托一定没有返回值,Func有返回值 */ // 1.无返回值,无参数 //public Action CmdAction { get; set; } // 2.没返回值,有参数 //public Action<string> CmdAction { get; set; } /// 3.有返回值,无参数 //public Func<int> CmdFunc { get; set; } // 4.有返回值,有参数 // Func<A1, A2..., B> 中A是参数,B是返回值 public Func<string, int> CmdFunc { get; set; } /// <summary> /// 能不能执行(执行业务) /// </summary> /// <param name="parameter"></param> public bool CanExecute(object? parameter) { if (parameter == null) { // 如果参数为空,这个控件(按钮)你往死里点也不会发生什么事 return false; } return true; } /// <summary> /// 如何执行(命令状态) /// </summary> /// <param name="parameter"></param> public void Execute(object? parameter) { //if (CmdAction != null) //{ // /* 执行委托 */ // // 1.无返回值+无参数 // //CmdAction.Invoke(); // // 2.无返回值 + 有参数 // CmdAction.Invoke(parameter.ToString()); //} if (CmdFunc != null) { /* 执行委托 */ //// 3.有返回值+无参数 //int num = CmdFunc.Invoke(); // 4.有返回值+无参数 int num = CmdFunc.Invoke(parameter.ToString()); MessageBox.Show($"{num}"); } //MessageBox.Show($"( •̀ ω •́ )ก็็็็็็็็็็็็็ {parameter}"); } } }
    • CustCmd_NotBus.xaml.cs

      • using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace Command_Demo { /// <summary> /// CustCmd_NotBus.xaml 的交互逻辑 /// </summary> public partial class CustCmd_NotBus : Window { public CustCmd_NotBus() { InitializeComponent(); } /// 1.测试 无返回值+无参数 的Action委托 //public void TestCmd() => MessageBox.Show("测试!!!"); /// 2.测试 无返回值+有参数 的Action委托 //public void TestCmd(string str) => MessageBox.Show($"测试参数:{str}"); //// 3.测试 有返回值+无参数 的Func委托 //public int TestCmd() //{ // MessageBox.Show("测试成功"); // return 1; //} // 4.测试 有返回值+有参数 的Func委托 public int TestCmd(string str) { MessageBox.Show($"测试参数:{str}"); return 1; } } }

四.本章小结

  • 命令执行过程流程图

    • 用户操作(点击按钮 / 菜单 / 快捷键) │ ▼ 命令源(Command Source) Button / MenuItem / KeyBinding │ │ # 触发 Command(命令) ▼ Command(继承ICommand) │ │ # 传递 CommandParameter(参数) ▼ 查找命令绑定(路由机制) # 从当前控件 → 向上冒泡查找 │ ▼ CommandBinding(命令绑定) │ │ │ │ ▼ ▼ CanExecute() Executed() # 能不能执行 执行逻辑 │ │ 控制UI是否可用(按钮是否禁用) ▼ CommandTarget(命令目标) (命令作用对象)

  • 概念本质作用关键点常见对象
    Command行为对象定义“做什么”Execute / CanExecuteICommand
    CommandSource触发者谁发起命令UI交互入口Button / Menu
    CommandBinding逻辑绑定在哪里执行Executed / CanExecuteWindow / 控件
    CommandTarget作用对象对谁生效默认是焦点控件TextBox / ListBox
    CommandParameter输入参数传递数据object类型SelectedItem 等

  • 类型本质是否路由是否常用场景
    ICommand接口⭐⭐⭐⭐⭐MVVM / 业务逻辑
    RoutedCommand路由命令⭐⭐多入口 / 复杂UI
    RoutedUICommandUI增强版⭐⭐菜单 / 本地化
    预定义命令官方命令⭐⭐⭐Copy / Paste / New

  • 命令的本质

    • 命令不是事件的上位替代,命令不是事件的上位替代,命令不是事件的上位替代,重要的事情说三遍
    • 命令实际上是对""行为"的对象化抽象,重要作用是前后端解耦
    • 核心作用:解耦 UI触发 → 执行逻辑 → 作用对象
    • 同时通过CanExecute实现状态驱动的交互控制

  • 命令系统的四个核心概念

    命令源 → 命令 → 命令绑定 → 命令目标 # 命令源:谁触发 # 命令:做什么 # 命令绑定:在哪里执行 # 命令目标:对谁生效

  • 命令源三大核心属性
    • Command:指定执行哪个命令(必须)
    • CommandParameter:传递执行参数(数据驱动行为)
    • CommandTarget:指定作用对象(默认是焦点控件)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询