当前位置:   article > 正文

WPF Command(命令)详解

wpf command

        WPF命令是一个任务的完整封装,例如保存,复制,剪切这些操作都可以理解为一个个独立的命令。乍一看,命令和传统的事件机制似乎很相似,但命令和事件并不冲突,命令和事件的区别就在于命令是具有约束力

WPF命令系统模型主要由以下四个元素组成:

  • 命令(Command):命令表示一个任务单元,并且可跟踪该任务的状态,实际上是实现了ICommand接口的类。
  • 命令源(CommandSource):即命令的发送者/行为的触发者,实际上是实现了ICommandSource接口的类。
  • 命令目标(CommandTarget):命令的接收者/命令的作用对象,命令目标必须是实现了 IInputElement接口的类。
  • 命令关联(CommandBinding):负责把一些外围逻辑与命令关联起来,是将命令逻辑映射到命令的对象,包括命令是否可以执行前后的操作、命令执行前后的操作。

              

命令的特点如下:
复用: 统一命令逻辑,减少代码冗余,封装独立、可复用的任务执行单元。
分离: 通过命令可以使命令源和命令目标分离,减少代码耦合。
状态同步: 命令可以使得控件的启用状态和相应的命令状态保持同步,从而指示操作是否可用。

ICommand接口
WPF命令模型的核心是System.Windows.Input.ICommand接口,该接口定义了命令的工作原理。该接口包含两个方法和一个事件:

  1. public interface ICommand
  2. {
  3.     //触发程序事件处理
  4.     void Execute(object parameter);
  5.     //命令可用,返回true;命令不可用,返回false
  6.     bool CanExecute(object parameter);
  7.     //当命令状态改变时引发
  8.     event EventHandler CanExecuteChanged;
  9. }

RoutedCommand类
        当创建自己的命令时,不会直接实现ICommand接口;而是使用System.Windows.Input.RoutedCommand类,该类自动实现了ICommand接口。RoutedCommand类是WPF中唯一实现了ICommand接口的类。换句话说,所有WPF命令都是RoutedCommand类及其派生类的实例

        RoutedCommand类为事件冒泡和隧道添加了一些额外的基础结构。鉴于ICommand接口封装了命令的思想——可被触发的动作并可被启用或禁用——RoutedCommand类对命令进行了修改,使命令可在WPF元素层次结构中冒泡,以便获得正确的事件处理程序。
  为支持路由事件,RoutedCommand类私有地实现了ICommand接口,并添加了ICommand接口方法的一些不同版本。最明显的变化是,Execute()和CanExecute()方法使用了一个额外参数。下面的新的签名:

  1. public void Execute(object parameter,IInputElement target)
  2. {
  3.     //...
  4. }
  5.  
  6. public void CanExecute(object parameter,IInputElement target)
  7. {
  8.     //...
  9. }

        参数target是开始处理事件的元素。事件从target元素开始,然后冒泡至高层的容器,直到应用程序为了执行合适的任务而处理了事件(为了处理Executed事件,元素还需要借助于另一个类——CommandBinding类的帮助)。
  除上面的修改外,RoutedCommand类还引入了三个属性:命令名称(Name属性)、包含命令的类(OwnerType)以及任何可用于触发命令的按键或鼠标操作(位于InputGestures集合中)。

RoutedUICommand类
        在程序中处理的大部分命令不是RoutedCommand对象,而是RoutedUICommand类的实例,RoutedUICommand类继承自RoutedCommand类(实际上,WPF提供的所有预先构建好的命令都是RoutedUICommand对象)。
  RoutedUICommand类用于具有文本的命令,这些文本显示在用户界面中的某些地方(例如菜单项文本、工具栏按钮的工具栏提示)。RoutedUICommand类只添加了Text属性,该属性是为命令显示的文本。
  为命令定义命令文本(而不是直接在控件上定义文本)的优点是可在某个位置执行本地化。但如果命令文本永远不会在用户界面的任何地方显示,那么RoutedUICommand类和RoutedCommand类是等效的。

以上三个类继承结构为RoutedUICommand->RoutedCommand->ICommand

三种命令实现方式可参考:WPF之命令Command - 简书

WPF命令库
        WPF设计者认识到,每个应用程序可能都有大量命令,并且对于许多不同的应用程序,很多命令时通用的,例如,所有基于文档的应用程序都有他们自己版本的New、Open以及Save命令。为减少创建这些命令所需的工作,WPF提供了基本命令库,基本命令库中保存的命令超过100条。这些命令通过以下5个专门的静态类的属性提供:

  •   ApplicationCommands:该类提供了通用命令,包括剪贴板命令(如Copy、Cut和Paste)以及文档命令(如New、Open、Save、SaveAs和Print等)。
  •   NavigationCommands:该类提供了用于导航的命令,包括为基于页面的应用程序设计的一些命令(如BrowseBack、BrowSeForward和NextPage),以及其他适合于基于文档的应用程序的命令(如IncreaseZoom和Refresh).
  •   EditingCommands:该类提供了许多重要的文档编辑命令,包括用于移动的命令(MoveToLineEnd、MoveLeftByWord和MoveUpByPage等),选择内容的命令(SelectToLineEnd、SelectLeftByWord),以及改变格式的命令(ToggleBold和ToggleUnderLine)。
  •   ComponentCommands:该类提供了由用户界面组件使用的命令,包括用于移动和选择内容的命令,这些命令和EditingCommands类中的一些命令类似(甚至完全相同)。
  •   MediaCommands:该类提供了一组用于处理多媒体的命令(如Play、Pause、NextTrack以及IncreaseVolume).

ApplicationCommands类提供了一组基本命令,在所有类别的应用程序中都经常会用到这些命令:New、Open、Close、Copy、Cut、Paste、Save、Delete、Stop等,如果你的程序中需要这些命令,那就不需要自己声明,直接拿来使用就可以。

以下示例显示了如何设置 MenuItem,以便在单击时它将调用 TextBox 上的 Paste 命令,假定 TextBox 具有键盘焦点:

  1. <StackPanel>
  2. <Menu>
  3. <MenuItem Command="ApplicationCommands.Paste" />
  4. </Menu>
  5. <TextBox />
  6. </StackPanel>

无Command属性控件实现Command绑定:

方法一:使用InputBingdings(包含KeyBinding和MouseBinding)

  1. <!--没有Command属性的控件可以使用KeyBinding或MouseBinding来绑定命令-->
  2. <TextBox Text="KeyBinding and MouseBinding" Height="40">
  3. <TextBox.InputBindings>
  4. <!--按下Alt+D,最大化窗体-->
  5. <KeyBinding Key="D" Modifiers="Alt" Command="SystemCommands.MaximizeWindowCommand"></KeyBinding>
  6. <!--KeyBinding绑定对象需要可聚焦,MouseBinding中MouseAction的优先级高于Gesture,当MouseAction设置以后,Gesture将失效-->
  7. <MouseBinding Gesture="LeftClick" MouseAction="RightClick" Command="SystemCommands.RestoreWindowCommand"></MouseBinding>
  8. </TextBox.InputBindings>
  9. </TextBox>

方法二:使用Microsoft.Xaml.Behaviors库(System.Windows.Interactivity已被弃用)

  1. <Window x:Class="WpfCommandDemo.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:local="clr-namespace:WpfCommandDemo"
  7. xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
  8. mc:Ignorable="d" WindowStartupLocation="CenterScreen"
  9. Title="MainWindow" Height="450" Width="800">
  10. <Grid>
  11. <Border Width="100" Height="40" Grid.Row="1" Background="LightBlue">
  12. <i:Interaction.Triggers>
  13. <i:EventTrigger EventName="MouseLeftButtonDown">
  14. <i:InvokeCommandAction Command="{Binding SelectedCommand}" CommandParameter="Microsoft.Xaml.Behaviors"/>
  15. </i:EventTrigger>
  16. </i:Interaction.Triggers>
  17. </Border>
  18. </Grid>
  19. </Window>

方法三:使用附加属性绑定命令

后台:

  1. public class ElementAttachProperty
  2. {
  3. public static ICommand GetTextBoxItemSelectCommand(DependencyObject obj)
  4. {
  5. return (ICommand)obj.GetValue(TextBoxItemSelectCommandProperty);
  6. }
  7. public static void SetTextBoxItemSelectCommand(DependencyObject obj, ICommand value)
  8. {
  9. obj.SetValue(TextBoxItemSelectCommandProperty, value);
  10. }
  11. public static readonly DependencyProperty TextBoxItemSelectCommandProperty =
  12. DependencyProperty.RegisterAttached("TextBoxItemSelectCommand", typeof(ICommand), typeof(ElementAttachProperty), new PropertyMetadata(OnTextBoxItemSelectCommand));
  13. private static void OnTextBoxItemSelectCommand(DependencyObject d, DependencyPropertyChangedEventArgs e)
  14. {
  15. var element = d as TextBox;
  16. element.TextChanged += Element_ItemSelecting;
  17. }
  18. private static void Element_ItemSelecting(object sender, EventArgs e)
  19. {
  20. var element = sender as TextBox;
  21. var command = (ICommand)element.GetValue(TextBoxItemSelectCommandProperty);
  22. command?.Execute(element.Text);
  23. }
  24. }
  25. public class MainViewModel
  26. {
  27. public MainViewModel()
  28. {
  29. }
  30. public ICommand ItemSelectedCmd
  31. {
  32. get { return new DelegateCommand(ItemSelected); }
  33. }
  34. private void ItemSelected(object o)
  35. {
  36. MessageBox.Show($"Command param is {o}");
  37. }
  38. }

前台:

 <TextBox Width="100" Height="30"   local:ElementAttachProperty.TextBoxItemSelectCommand="{Binding ItemSelectedCmd}" Margin="0,10,0,0"/>

使用自定义控件添加COMMAND依赖属性传递命令:

        右键项目添加"WPF自定义控件",命名CommandControl,在项目中会生成一个Themes文件夹,存放自定义控件样式主题文件Generic.xaml。具体代码如下:

CommandControl.cs文件:

  1. public class CommandControl : ContentControl
  2. {
  3. static CommandControl()
  4. {
  5. DefaultStyleKeyProperty.OverrideMetadata(typeof(CommandControl), new FrameworkPropertyMetadata(typeof(CommandControl)));
  6. }
  7. public CommandControl()
  8. {
  9. MouseLeftButtonDown += CommandControl_MouseLeftButtonDown;
  10. }
  11. private void CommandControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  12. {
  13. if (Command != null)
  14. {
  15. if (Command.CanExecute(CommandParameter))
  16. {
  17. Command.Execute(CommandParameter);
  18. }
  19. }
  20. }
  21. private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
  22. {
  23. if (Command != null)
  24. {
  25. if (Command.CanExecute(CommandParameter))
  26. {
  27. Command.Execute(CommandParameter);
  28. }
  29. }
  30. }
  31. public static readonly DependencyProperty CommandProperty =
  32. DependencyProperty.Register("Command", typeof(ICommand),
  33. typeof(CommandControl),
  34. new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
  35. public ICommand Command
  36. {
  37. get { return (ICommand)GetValue(CommandProperty); }
  38. set { SetValue(CommandProperty, value); }
  39. }
  40. public static readonly DependencyProperty CommandParameterProperty =
  41. DependencyProperty.Register("CommandParameter", typeof(object),
  42. typeof(CommandControl),
  43. new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
  44. public object CommandParameter
  45. {
  46. get { return (object)GetValue(CommandParameterProperty); }
  47. set { SetValue(CommandParameterProperty, value); }
  48. }
  49. }

Generic.xaml样式主题文件:

  1. <ResourceDictionary
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:local="clr-namespace:WpfCommandDemo">
  5. <Style TargetType="{x:Type local:CommandControl}">
  6. <Setter Property="Template">
  7. <Setter.Value>
  8. <ControlTemplate TargetType="{x:Type local:CommandControl}">
  9. <ContentControl>
  10. <ContentPresenter/>
  11. </ContentControl>
  12. </ControlTemplate>
  13. </Setter.Value>
  14. </Setter>
  15. </Style>
  16. </ResourceDictionary>

自定义控件调用:

  1. <Border Grid.Row="1" Grid.Column="1" Width="100" Height="40" Background="LightCoral">
  2. <local:CommandControl Command="{Binding ItemSelectedCmd}" CommandParameter="自定义控件命令">
  3. <TextBlock Text="UserCommand"/>
  4. </local:CommandControl>
  5. </Border>

参考:https://www.shuzhiduo.com/A/kmzLM3lbJG/

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/493453
推荐阅读
相关标签
  

闽ICP备14008679号