赞
踩
WPF命令是一个任务的完整封装,例如保存,复制,剪切这些操作都可以理解为一个个独立的命令。乍一看,命令和传统的事件机制似乎很相似,但命令和事件并不冲突,命令和事件的区别就在于命令是具有约束力。
WPF命令系统模型主要由以下四个元素组成:
命令的特点如下:
复用: 统一命令逻辑,减少代码冗余,封装独立、可复用的任务执行单元。
分离: 通过命令可以使命令源和命令目标分离,减少代码耦合。
状态同步: 命令可以使得控件的启用状态和相应的命令状态保持同步,从而指示操作是否可用。
ICommand接口
WPF命令模型的核心是System.Windows.Input.ICommand接口,该接口定义了命令的工作原理。该接口包含两个方法和一个事件:
- public interface ICommand
- {
- //触发程序事件处理
- void Execute(object parameter);
- //命令可用,返回true;命令不可用,返回false
- bool CanExecute(object parameter);
- //当命令状态改变时引发
- event EventHandler CanExecuteChanged;
- }
RoutedCommand类
当创建自己的命令时,不会直接实现ICommand接口;而是使用System.Windows.Input.RoutedCommand类,该类自动实现了ICommand接口。RoutedCommand类是WPF中唯一实现了ICommand接口的类。换句话说,所有WPF命令都是RoutedCommand类及其派生类的实例。
RoutedCommand类为事件冒泡和隧道添加了一些额外的基础结构。鉴于ICommand接口封装了命令的思想——可被触发的动作并可被启用或禁用——RoutedCommand类对命令进行了修改,使命令可在WPF元素层次结构中冒泡,以便获得正确的事件处理程序。
为支持路由事件,RoutedCommand类私有地实现了ICommand接口,并添加了ICommand接口方法的一些不同版本。最明显的变化是,Execute()和CanExecute()方法使用了一个额外参数。下面的新的签名:
- public void Execute(object parameter,IInputElement target)
- {
- //...
- }
-
- public void CanExecute(object parameter,IInputElement target)
- {
- //...
- }
参数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类提供了一组基本命令,在所有类别的应用程序中都经常会用到这些命令:New、Open、Close、Copy、Cut、Paste、Save、Delete、Stop等,如果你的程序中需要这些命令,那就不需要自己声明,直接拿来使用就可以。
以下示例显示了如何设置 MenuItem,以便在单击时它将调用 TextBox 上的 Paste 命令,假定 TextBox 具有键盘焦点:
- <StackPanel>
- <Menu>
- <MenuItem Command="ApplicationCommands.Paste" />
- </Menu>
- <TextBox />
- </StackPanel>
无Command属性控件实现Command绑定:
方法一:使用InputBingdings(包含KeyBinding和MouseBinding)
- <!--没有Command属性的控件可以使用KeyBinding或MouseBinding来绑定命令-->
- <TextBox Text="KeyBinding and MouseBinding" Height="40">
- <TextBox.InputBindings>
- <!--按下Alt+D,最大化窗体-->
- <KeyBinding Key="D" Modifiers="Alt" Command="SystemCommands.MaximizeWindowCommand"></KeyBinding>
- <!--KeyBinding绑定对象需要可聚焦,MouseBinding中MouseAction的优先级高于Gesture,当MouseAction设置以后,Gesture将失效-->
- <MouseBinding Gesture="LeftClick" MouseAction="RightClick" Command="SystemCommands.RestoreWindowCommand"></MouseBinding>
- </TextBox.InputBindings>
- </TextBox>
方法二:使用Microsoft.Xaml.Behaviors库(System.Windows.Interactivity已被弃用)
- <Window x:Class="WpfCommandDemo.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:local="clr-namespace:WpfCommandDemo"
- xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
- mc:Ignorable="d" WindowStartupLocation="CenterScreen"
- Title="MainWindow" Height="450" Width="800">
- <Grid>
- <Border Width="100" Height="40" Grid.Row="1" Background="LightBlue">
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="MouseLeftButtonDown">
- <i:InvokeCommandAction Command="{Binding SelectedCommand}" CommandParameter="Microsoft.Xaml.Behaviors"/>
- </i:EventTrigger>
- </i:Interaction.Triggers>
- </Border>
- </Grid>
- </Window>
方法三:使用附加属性绑定命令
后台:
-
- public class ElementAttachProperty
- {
- public static ICommand GetTextBoxItemSelectCommand(DependencyObject obj)
- {
- return (ICommand)obj.GetValue(TextBoxItemSelectCommandProperty);
- }
-
- public static void SetTextBoxItemSelectCommand(DependencyObject obj, ICommand value)
- {
- obj.SetValue(TextBoxItemSelectCommandProperty, value);
- }
-
- public static readonly DependencyProperty TextBoxItemSelectCommandProperty =
- DependencyProperty.RegisterAttached("TextBoxItemSelectCommand", typeof(ICommand), typeof(ElementAttachProperty), new PropertyMetadata(OnTextBoxItemSelectCommand));
-
- private static void OnTextBoxItemSelectCommand(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var element = d as TextBox;
- element.TextChanged += Element_ItemSelecting;
- }
-
- private static void Element_ItemSelecting(object sender, EventArgs e)
- {
- var element = sender as TextBox;
- var command = (ICommand)element.GetValue(TextBoxItemSelectCommandProperty);
- command?.Execute(element.Text);
- }
- }
-
- public class MainViewModel
- {
- public MainViewModel()
- {
-
- }
- public ICommand ItemSelectedCmd
- {
- get { return new DelegateCommand(ItemSelected); }
- }
- private void ItemSelected(object o)
- {
- MessageBox.Show($"Command param is {o}");
- }
- }
前台:
<TextBox Width="100" Height="30" local:ElementAttachProperty.TextBoxItemSelectCommand="{Binding ItemSelectedCmd}" Margin="0,10,0,0"/>
使用自定义控件添加COMMAND依赖属性传递命令:
右键项目添加"WPF自定义控件",命名CommandControl,在项目中会生成一个Themes文件夹,存放自定义控件样式主题文件Generic.xaml。具体代码如下:
CommandControl.cs文件:
- public class CommandControl : ContentControl
- {
- static CommandControl()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(CommandControl), new FrameworkPropertyMetadata(typeof(CommandControl)));
- }
-
- public CommandControl()
- {
- MouseLeftButtonDown += CommandControl_MouseLeftButtonDown;
- }
-
- private void CommandControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
- {
- if (Command != null)
- {
- if (Command.CanExecute(CommandParameter))
- {
- Command.Execute(CommandParameter);
- }
- }
- }
-
- private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
- {
- if (Command != null)
- {
- if (Command.CanExecute(CommandParameter))
- {
- Command.Execute(CommandParameter);
- }
- }
- }
-
- public static readonly DependencyProperty CommandProperty =
- DependencyProperty.Register("Command", typeof(ICommand),
- typeof(CommandControl),
- new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
-
- public ICommand Command
- {
- get { return (ICommand)GetValue(CommandProperty); }
- set { SetValue(CommandProperty, value); }
- }
-
- public static readonly DependencyProperty CommandParameterProperty =
- DependencyProperty.Register("CommandParameter", typeof(object),
- typeof(CommandControl),
- new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
-
- public object CommandParameter
- {
- get { return (object)GetValue(CommandParameterProperty); }
- set { SetValue(CommandParameterProperty, value); }
- }
-
- }
Generic.xaml样式主题文件:
- <ResourceDictionary
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:WpfCommandDemo">
- <Style TargetType="{x:Type local:CommandControl}">
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type local:CommandControl}">
- <ContentControl>
- <ContentPresenter/>
- </ContentControl>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- </Style>
- </ResourceDictionary>
自定义控件调用:
- <Border Grid.Row="1" Grid.Column="1" Width="100" Height="40" Background="LightCoral">
- <local:CommandControl Command="{Binding ItemSelectedCmd}" CommandParameter="自定义控件命令">
- <TextBlock Text="UserCommand"/>
- </local:CommandControl>
-
- </Border>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。