【WPF】命令系统
引言
在MVVM模式开发下,命令Command是编程中不可或缺的一部分.下面,我分3种场景简单介绍一下命令的用法.
ViewModel中的命令
在ViewModel定义命令是最常用的用法,开发中几乎90%以上的命令都在用在ViewModel上.怎么用?先从实现ICommand说起,下面定义一个命令
public class MyCommand :ICommand { public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { MessageBox.Show("命令已经执行!"); } }
然后在ViewModel中定义一个命令,赋值MyCommand,接着在View绑定命令,如下
public class MainWindowViewModel { public MainWindowViewModel() { myCommand = new MyCommand(); } public ICommand myCommand {get ; set ; } }
<Window x:Class="WpfCommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfCommand" Title="MainWindow" Height="350" Width="525"> <Grid> <Button Command="{Binding myCommand}" Name="btn" Height="30" Width="80" Margin="104,75,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">执行命令</Button> </Grid> </Window>
上面看起来不复杂,但是实际开发绝对不这么干的,如果每个命令都要自定义类,还能好好玩耍么.实际开发,我们可以借助第三方类轻松完成我们的命令实现,如Prism框架的DelegateCommand,MVVMLight的RelayCommand,下面演示一下DelegateCommand,只需在ViewModel中实例化一个命令既可.
public class MainWindowViewModel { public MainWindowViewModel() { myCommand = new DelegateCommand(() => { MessageBox.Show("命令已经执行!"); }, () => true); } public ICommand myCommand {get ; set ; } }
自定义控件中的命令绑定
在自定义控件中绑定命令不常见,但是某些场景下还是有用的.例如DataGrid的新增行,删除行,绑定自定义命令,让外部调用还是很有用的.下面,用继承TextBox做个演示吧.
利用CommandManager绑定命令,命令的作用是将字体变成红色,无文本时则命令不可用.MyTextBox定义如下
public class MyTextBox:TextBox { static MyTextBox() { CommandManager.RegisterClassCommandBinding(typeof(MyTextBox), new CommandBinding(MyCommands.MyRoutedCommand, Command_Executed, Command_CanExecute)); } private static void Command_CanExecute(object sender, CanExecuteRoutedEventArgs e) { MyTextBox myTextBox = (MyTextBox)sender; e.CanExecute = !string.IsNullOrWhiteSpace(myTextBox.Text); } private static void Command_Executed(object sender, ExecutedRoutedEventArgs e) { MyTextBox myTextBox = (MyTextBox)sender; myTextBox.Foreground = new SolidColorBrush(Colors.Red); } }
public class MyCommands { private static RoutedUICommand myRoutedCommand; static MyCommands() { myRoutedCommand = new RoutedUICommand( "MyRoutedCommand", "MyRoutedCommand", typeof(MyCommands)); } public static RoutedUICommand MyRoutedCommand { get { return myRoutedCommand; } } }
如何使用,很简单,设置一下Button的Command和CommandTarget即可,如下
<Window x:Class="WpfCommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfCommand" Title="MainWindow" Height="350" Width="525"> <Grid> <local:MyTextBox x:Name="mytextbox"></local:MyTextBox> <Button Command="local:MyCommands.MyRoutedCommand" CommandTarget="{Binding ElementName=mytextbox}" Name="btn" Height="30" Width="80" Margin="104,75,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">执行命令</Button> </Grid> </Window>
自定义控件中的命令属性
在WPF的控件库,我们会发现很多控件都带有Command,例如Button.控件中有Command属性才能让我们如期望地执行命令.如果我们的自定义控件想定义这个Command,就要实现ICommandSource了.下面从定义一个简单按钮来演示一下.
public class MyButton : ContentControl,ICommandSource { public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof(ICommand), typeof(MyButton), new PropertyMetadata((ICommand)null, new PropertyChangedCallback(CommandChanged))); public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register( "CommandTarget", typeof(IInputElement), typeof(MyButton), new PropertyMetadata((IInputElement)null)); public IInputElement CommandTarget { get { return (IInputElement)GetValue(CommandTargetProperty); } set { SetValue(CommandTargetProperty, value); } } public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register( "CommandParameter", typeof(object), typeof(MyButton), new PropertyMetadata((object)null)); public object CommandParameter { get { return (object)GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } private static void CommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { } //在鼠标的按下事件中,调用命令的执行 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); if (!Command.CanExecute(CommandParameter)) return; Command.Execute(CommandParameter); } }
上面只是简单地定义了命令,实际上Button的Command定义要比上面复杂些,有兴趣的童鞋可以去看看Button的源码,推荐一款反编译神器dotPeek.下面是Xaml的定义.
<Window x:Class="WpfCommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfCommand" Title="MainWindow" Height="350" Width="525"> <Grid> <local:MyTextBox x:Name="mytextbox"></local:MyTextBox> <local:MyButton Height="50" Width="80" Command="local:MyCommands.MyRoutedCommand" CommandTarget="{Binding ElementName=mytextbox}" > <Ellipse Fill="Blue"> </Ellipse> </local:MyButton> </Grid> </Window>
小结
本着从实际出发的原则,命令的介绍和其他内容被我省略了.最后,如果你有更好的想法,请不吝留言指教.