心如止水

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

首先介绍CommandManager类,它有一个重要的静态事件:
RequerySuggested: Occurs when the CommandManager detects conditions that might change the ability of a command to execute.
当CommandManager认为当前的某个改变或动作有可能会改变command的能否执行的状态时,就触发该事件。例如焦点改变,所以这个事件会多次被触发。

另外有一个重要的静态方法:
InvalidateRequerySuggested(): Forces the CommandManager to raise the RequerySuggested event.
手动的调用这个方法强制的触发RequerySuggested事件。

如下是测试的代码,xaml:

View Code
<Window x:Class="RelayAndDelegateCommand.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" xmlns:local="clr-namespace:RelayAndDelegateCommand">
    <StackPanel>
        <Button Content="Test Routed Command" Command="{x:Static local:MainWindow.TestRoutedCommand}"  />
        <Button Margin="0,8,0,0" Content="Test Relay Command" Command="{Binding TestRelayCommand}" />
        <Button Margin="0,8,0,0" Content="Test Delegate Command" Command="{Binding TestDelegateCommand}"  />
        <Button  Margin="0,20,0,0" Content="Click me" HorizontalAlignment="Center" Name="button1" VerticalAlignment="Top" Width="80" Click="button1_Click" />
    </StackPanel>
</Window>

code-main window:

View Code
public partial class MainWindow : Window
    {
        private bool _cansave = false;

        public MainWindow()
        {
            InitializeComponent();
            this.CommandBindings.Add(new CommandBinding(TestRoutedCommand, new ExecutedRoutedEventHandler(OnTestRoutedCommandExecuted), new CanExecuteRoutedEventHandler(OnTestRoutedCommandCanExecute)));
            this.DataContext = this;
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            _cansave = true;
            // DelegateCommand needs manually raise can execute changed.
            (TestDelegateCommand as DelegateCommand).RaiseCanExecuteChanged();
        }

        #region 1. TestRoutedCommand

        public static readonly RoutedCommand TestRoutedCommand = new RoutedCommand();

        public void OnTestRoutedCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Hello world from RoutedCommand");
        }

        public void OnTestRoutedCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _cansave;
            Debug.WriteLine("CanExecute from RoutedCommand");
        }

        #endregion

        #region 2. TestRelayCommand

        private ICommand _testRelayCommand;
        public ICommand TestRelayCommand
        {
            get
            {
                if (_testRelayCommand == null)
                {
                    _testRelayCommand = new RelayCommand(new Action<object>(OnTestRelayCommandExecuted), new Predicate<object>(OnTestRelayCommandCanExecute));
                }
                return _testRelayCommand;
            }
        }

        public void OnTestRelayCommandExecuted(object para)
        {
            MessageBox.Show("Hello world from RelayCommand");
        }

        public bool OnTestRelayCommandCanExecute(object para)
        {
            Debug.WriteLine("CanExecute from RelayCommand");
            return _cansave;
        }

        #endregion

        #region 3. TestDelegateCommand

        private ICommand _testDelegateCommand;
        public ICommand TestDelegateCommand
        {
            get
            {
                if (_testDelegateCommand == null)
                {
                    _testDelegateCommand = new DelegateCommand(new Action<object>(OnTestDelegateCommandExecuted), new Predicate<object>(OnTestDelegateCommandCanExecute));
                }
                return _testDelegateCommand;
            }
        }

        public void OnTestDelegateCommandExecuted(object para)
        {
            MessageBox.Show("Hello world from DelegateCommand");
        }

        public bool OnTestDelegateCommandCanExecute(object para)
        {
            Debug.WriteLine("CanExecute from DelegateCommand");
            return _cansave;
        }

        #endregion
    }

code-一个简单的RelayCommand类:

View Code
public class RelayCommand : ICommand
    {
        #region Fields

        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        #endregion

        #region Constructors

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        #endregion

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        // When command manager thinks the canexecute might change(e.g. focus changed), it raises RequerySuggested event.
        // The CanExecuteChanged is automatically registered by command binding, the execution logic of updating the button's
        // enabled\disabled state(value below) which is usually executed when CanExecuteChanged triggered, now is delegated to
        // RequerySuggested event, so when RequerySuggested triggered, the execution logic is being executed, and button's state gets updated.
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        #endregion
    }

code-一个简单的DelegateCommand类:

View Code
public class DelegateCommand : ICommand
    {
        #region Fields

        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        #endregion

        #region Constructors

        public DelegateCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        #endregion

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
        public event EventHandler CanExecuteChanged;

        // The CanExecuteChanged is automatically registered by command binding, we can assume that it has some execution logic 
        // to update the button's enabled\disabled state(though we cannot see). So raises this event will cause the button's state be updated.
        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        #endregion
    }

在这个例子中,第一个RoutedCommand是系统定义的,后面两个是经常看到的两个自定义的command的实现,它们都要实现

ICommand接口。这个接口有一个重要的事件public event EventHandler CanExecuteChanged,command binding机制内部会

自动的注册这个事件,当我们触发这个事件的时候,command binding机制内部会执行相应的逻辑来更新该命令可用不可用的状态(如上面的DelegateCommand的实现)。

另一种实现方式如上面的RelayCommand所示,

public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

它把更新命令可用不可用的状态的逻辑(上面代码中的value)代理给了CommandManager.RequerySuggested事件,而这个事件的触发是由CommandManager自己来检测的,当RequerySuggested事件被触发时,执行同样的逻辑(上面的value),command同样得到刷新。

该实现与DelegateCommand的不同是DelegateCommand需要自己手动的调用RaiseCanExecuteChanged()方法来刷新,而RelayCommand的实现是一种懒的方式,不需要自己调用,由系统检测。
这种懒的方式带来的问题就是导致CanExecute方法多次被执行,例如上面说到的焦点改变时,可能会带来性能影响。
如果查看RoutedCommand的实现,可以发现它的实现和RelayCommand是一样的,所以平时我们使用它的时候并不需要手动的
通知这个命令刷新了。

RoutedCommand的内部实现:

 

INotifyPropertyChanged接口的PropertyChanged事件和ICommand接口的CanExecuteChanged事件类似,
通常在一个数据绑定中,我们并没有去注册这个事件,而我们经常调用如下的方法去通知某个地方要去做些更新的动作了。
protected void OnPropertyChanged(string propertyName)
{
    if (this.PropertyChanged != null)
         this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
那么是谁注册这个事件去做一些事情呢?答案是数据绑定机制内部去注册的,所以我们只管触发这个事件发送一个通知就好了。

 

示例下载:https://files.cnblogs.com/bear831204/RelayAndDelegateCommand.zip

posted on 2012-05-31 15:27  cutebear  阅读(5450)  评论(0编辑  收藏  举报