RoutedComand\RelayCommand\DelegateCommand 的实现比较

首先介绍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的内部实现:

RoutedComand\RelayCommand\DelegateCommand 的实现比较

 

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

 

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

你可能感兴趣的:(delegate)