WPF Command 命令

参考

环境

软件/系统 版本 说明
Windows Windows 10 专业版 22H2 19045.4046
Microsoft Visual Studio Microsoft Visual Studio Community 2022 (64 位) - 17.6.5
Microsoft .Net SDK 8.0.101 手动安装
Microsoft .Net SDK 7.0.306 Microsoft Visual Studio 携带
.net 6.x 创建当前文章演示 WPF 项目时指定 .net 版本所选择的框架

正文

概念

WPF 中的路由命令模型可分解为四个主要概念:命令、命令源、命令目标和命令绑定:

  • 命令是要执行的操作。
  • 命令源是调用命令的对象,命令源通常实现 ICommandSource 接口。。
  • 命令目标是在其上执行命令的对象,命令目标是 Executed 和 CanExecute 的路由开始的元素。仅当 ICommand 为 RoutedCommand 时,ICommandSource 上的 CommandTarget 属性才适用 如果在 ICommandSource 上设置 CommandTarget 并且相应的命令不是 RoutedCommand,则忽略命令目标。命令源可以显式设置命令目标。
  • 命令绑定是将命令逻辑映射到命令的对象。

WPF 中的命令是通过实现 ICommand 接口创建的。 ICommand 公开了两种方法 Execute 和 CanExecute,以及一个事件 CanExecuteChanged。 Execute 执行与该命令关联的操作。 CanExecute 确定是否可以在当前命令目标上执行该命令。 如果集中管理命令操作的命令管理器检测到命令源中存在一个可能使已引发命令无效但尚未由命令绑定执行的更改,则会引发 CanExecuteChanged。 ICommand 的 WPF 实现是 RoutedCommand 类。

许多这些命令都包含一组默认输入绑定。 例如,如果指定应用程序处理复制命令,则可自动获取键盘绑定“CTRL+C”。此外,还可获得其他输入设备的绑定,例如 Tablet PC 笔势和语音信息。

系统预置命令

名称 功能
ApplicationCommands 提供一组与应用程序相关的标准命令。
NavigationCommands 提供一组标准的与导航相关的命令。
MediaCommands 提供媒体相关命令的标准集。
EditingCommands 提供一组标准的编辑相关命令。
ComponentCommands 提供一组与组件相关的标准命令,这些命令具有预定义的按键输入笔势和 Text 属性。
  1. 以下示例显示了如何设置 MenuItem,以便在单击时它将调用 TextBox 上的 Paste 命令,假定 TextBox 具有键盘焦点。

    Paste 命令是命令,MenuItem 是命令源,TextBox 是命令目标,命令绑定由 TextBox 控件提供。 值得注意的是,CommandBinding 并不总是由作为命令目标类的控件提供。 通常,CommandBinding 必须由应用程序开发者创建,否则 CommandBinding 可能会附加到命令目标的上级元素。

    • 图片
      image
    • xaml
      <StackPanel>
          <Menu>
              <MenuItem Command="ApplicationCommands.Paste" />
          </Menu>
          <TextBox />
      </StackPanel>
      
  2. 显式设置命令目标。

    • 图片
      image

    • xaml

      <StackPanel>
          <Menu>
              <MenuItem Command="ApplicationCommands.Paste" CommandTarget="{Binding ElementName=mainTextBox1}" />
          </Menu>
          <TextBox Name="mainTextBox" />
          <TextBox Name="mainTextBox1" />
          <TextBox Name="mainTextBox2" />
      </StackPanel>
      

自定义命令

  • RoutedCommand
    image

    • xaml
      <Window
      	x:Class="学习.MainWindow"
      	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:local="clr-namespace:学习"
      	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      	xmlns:sys="clr-namespace:System;assembly=mscorlib"
      	Title="MainWindow"
      	Width="850"
      	Height="700"
      	mc:Ignorable="d">
      	<Window.Resources />
      	<Window.CommandBindings>
      		<CommandBinding
      			CanExecute="GetButtonContentCmdCanExecute"
      			Command="{x:Static local:MainWindow.GetButtonContentCmd}"
      			Executed="GetButtonContentCmdExecuted" />
      	</Window.CommandBindings>
      	<Grid>
      		<StackPanel>
      			<!--  RoutedCommand https://github.com/Microsoft/WPF-Samples/tree/main/Input%20and%20Commands/CustomRoutedCommand  -->
      			<Button Command="{x:Static local:MainWindow.GetButtonContentCmd}" Content="按钮1" />
      			<Button Command="{x:Static local:MainWindow.GetButtonContentCmd}" Content="按钮2" />
      			<Button Command="{x:Static local:MainWindow.GetButtonContentCmd}" Content="按钮3" />
      		</StackPanel>
      	</Grid>
      </Window>
      
      
    • c#
      using System.Text;
      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.Navigation;
      using System.Windows.Shapes;
      
      namespace 学习
      {
      	/// <summary>
      	/// Interaction logic for MainWindow.xaml
      	/// </summary>
      	public partial class MainWindow : Window
      	{
      
      		public static RoutedCommand GetButtonContentCmd = new RoutedCommand();
      
      		// 执行命令方法
      		private void GetButtonContentCmdExecuted(object sender, ExecutedRoutedEventArgs e)
      		{
      			// object sender 的值居然是 Window 对象
      			Button? b = e.Source as Button;
      			if (b != null)
      			{
      				MessageBox.Show(b.Content.ToString());
      			}
      		}
      
      		//判断是否可执行命令
      		private void GetButtonContentCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
      		{
      			e.CanExecute = true;
      		}
      
      		public MainWindow()
      		{
      			InitializeComponent();
      		}
      	}
      
      }
      
  • ICommand MVVM 模式
    image

    • MainWindow.xaml
      <Window
      	x:Class="学习.MainWindow"
      	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:local="clr-namespace:学习"
      	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      	xmlns:sys="clr-namespace:System;assembly=mscorlib"
      	Title="MainWindow"
      	Width="850"
      	Height="700"
      	mc:Ignorable="d">
      	<Window.DataContext>
      		<local:MainWindowViewModel />
      	</Window.DataContext>
      	<Grid>
      		<StackPanel>
      			<TextBlock
      				Width="120"
      				Height="60"
      				Text="{Binding Name}" />
      			<Button
      				Width="120"
      				Height="60"
      				Command="{Binding OnShowNameCmd}"
      				Content="点击" />
      		</StackPanel>
      
      	</Grid>
      </Window>
      
    • MainWindow.xaml.cs
      using System.Text;
      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.Navigation;
      using System.Windows.Shapes;
      
      namespace 学习
      {
      	/// <summary>
      	/// Interaction logic for MainWindow.xaml
      	/// </summary>
      	public partial class MainWindow : Window
      	{
      
      		public MainWindow()
      		{
      			InitializeComponent();
      		}
      	}
      
      }
      
    • MainWindowViewModel.cs
      using System;
      using System.Collections.Generic;
      using System.ComponentModel;
      using System.Diagnostics;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using System.Windows;
      using System.Windows.Input;
      
      namespace 学习
      {
      	// 继承 INotifyPropertyChanged 并在属性 set 时触发 PropertyChanged 则会通知前端同步修改为最新值
      	internal class MainWindowViewModel : INotifyPropertyChanged
      	{
      		public event PropertyChangedEventHandler? PropertyChanged;
      
      		// 
      		string name = "xiaqiuchu";
      		public string Name
      		{
      			get { return name; }
      			set
      			{
      				name = value;
      				PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
      			}
      		}
      
      		private void ShowName(object? parameter)
      		{
      			MessageBox.Show(name);
      		}
      
      		// 当前的软件版本,需要定义 get 才能够正确触发
      		public ICommand OnShowNameCmd { get; }
      
      		public MainWindowViewModel()
      		{
      			OnShowNameCmd = new OnShowNameCommand(ShowName);
      		}
      	}
      	public class OnShowNameCommand : ICommand
      	{
      		private Action<object?> _execute;
      		public OnShowNameCommand(Action<object?> execute)
      		{
      			_execute = execute;
      		}
      		public event EventHandler? CanExecuteChanged;
      
      		// 是否可以/符合执行事件
      		public bool CanExecute(object? parameter)
      		{
      			Trace.WriteLine("CanExecute");
      			return true;
      		}
      		// 事件执行方法
      		public void Execute(object? parameter)
      		{
      			Trace.WriteLine("Execute");
      			_execute(parameter);
      		}
      	}
      }
      
posted @   夏秋初  阅读(623)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
历史上的今天:
2022-03-01 Java 线程工具类 加法/减法/信号量
2022-03-01 Java 将线程不安全的集合转换为线程安全的集合
点击右上角即可分享
微信分享提示