当前位置: 首页 > news >正文

WPF新手村教程(六)— 新手村BOSS战前准备(命令)

WPF个人文档(六)—— 命令


命令最大的作用就是前后端解耦,它并不是事件的上位替代品!!!


一.概念

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

    • 命令:一种权威性的指示,通常指上级对下级的口头或书面指示,要求其执行某项任务或采取某种行动
      命令往往具有明确的目标和要求,并带有一定的强制性
  • 然后我们再来看看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; // 获取参数 CommandParameterMessageBox.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 / CanExecute ICommand
    CommandSource 触发者 谁发起命令 UI交互入口 Button / Menu
    CommandBinding 逻辑绑定 在哪里执行 Executed / CanExecute Window / 控件
    CommandTarget 作用对象 对谁生效 默认是焦点控件 TextBox / ListBox
    CommandParameter 输入参数 传递数据 object类型 SelectedItem 等

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

  • 命令的本质

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

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

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

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

这里几乎都是根据龙马老师的教学视频学习的,是我个人目前为止找到的,
感觉关于命令的最清晰的教学,所以这篇随笔是根据龙马老师教学框架写的笔记
建议结合视频观看,随笔根据其内容做出了一些补充
up主页:龙马008的个人空间-龙马008个人主页-哔哩哔哩视频

随笔参考:
1.WPF 自定义快捷键命令(Command) - Gnie - 博客园
2.《深入浅出WPF》笔记——命令篇 - haiziguo - 博客园
3.64.第9章_命令概念_哔哩哔哩_bilibili
4.65.第9章_命令相关的4个概念_哔哩哔哩_bilibili
5.67.第9章_简单的自定义命令_哔哩哔哩_bilibili
6.68.第9章_自定义命令(委托)_哔哩哔哩_bilibili
7.69.第9章_自定义命令(委托2)_哔哩哔哩_bilibili

给同事看了一下我的博客,他吐槽说,有一些地方AI味是不是有点浓,
例如那个什么1.0,2.0,还有代码里面的
我说那就是我的风格,几年前的笔记就开始这样了
同事:原来你是个AI
我:&!@*HHO!!#DOUI!UI!{:::》(鸟语花香)

http://www.jsqmd.com/news/523258/

相关文章:

  • 国标GB28181视频汇聚平台EasyCVR智慧社区全场景可视化管控与智能安防实践
  • 2025-2026年AI营销智能体公司推荐:应对市场波动与预算压力的智能决策伙伴盘点 - 品牌推荐
  • DM8数据库容灾避坑手册:从备份恢复到应急方案的全套操作实录(含PSEG_RECV参数详解)
  • C盘空间告急?保姆级教程:为Kali WSL2搬家到D盘并安装kali-linux-large工具包
  • 中小企业数字化转型,优先选 RPA 还是 AI Agent?:2026企业自动化架构选型深研
  • C语言游戏开发:Pygame、SDL、OpenGL深度解析
  • RecyclerView Demo - Android列表组件详解
  • SEO_ 内容营销中如何自然融合SEO关键词的策略
  • 网络协议分析(CTF 入门博客)
  • 2026年集装箱翻转机厂家推荐:山东金贯通用机械有限公司,移动式集装箱翻转机/双车道集装箱翻转机厂家精选 - 品牌推荐官
  • 国产CRM真实体验如何?2026年十大用户推荐CRM系统排行 - SaaS软件-点评
  • Java类间变量共享与进度更新的实现策略
  • 2026食品/餐饮虫控新选择:景隆智能灭蝇灯,用数据终结蝇害困扰 - 速递信息
  • keil将ANSI编码模式改为UTF-8编码模式方法
  • 用Anaconda玩转D2L教材:手把手教你同步李沐AI课程实验环境(Python3.8.5版)
  • 面向对象(下)
  • 基于 Spring AI Alibaba 搭建 Text-To-SQL 智能系统(简单实现)
  • SEO_掌握关键词研究的正确方法,驱动SEO流量增长
  • 2025-2026年数字化咨询公司推荐:战略规划与组织效能提升口碑机构盘点 - 品牌推荐
  • 2026年电磁式振动台渠道推荐,梵码仪器专业服务东莞苏州客户 - 工业品网
  • 本科生计算机视觉科研入门:顶刊盘点、里程碑论文与学习路线
  • Ansys Rocky 离散DEM颗粒流仿真,核心供应商推荐 - 品牌2026
  • 好用的工程瓷砖批发厂商有哪些,北京地区推荐 - 工业品牌热点
  • 微信小程序电商实战:前后端分离架构,20章吃透全栈开发+上线部署
  • 吊打面试官:手撕堆排序与 Top-K 问题的最优解
  • 2025-2026年数字化咨询公司推荐:集团型企业战略与数字化融合高价值伙伴指南 - 品牌推荐
  • 稳如磐石:STM32F4 与 DP83848 打造的以太网驱动工程
  • 2025-2026年中国精益生产咨询公司推荐:工厂现场改善口碑机构与长期合作价值分析 - 品牌推荐
  • 2025-2026年麻将机品牌前十名推荐:高端会所智能多功能热门款式与真实评价对比 - 品牌推荐
  • 什么是渗透测试工程师?(非常详细),零基础入门渗透测试,看这一篇就够了