• 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的内部实现:

    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

  • 相关阅读:
    MySQL5.6 单列、多列索引以及IN语句的优化(翻译)
    curl
    HTML meta
    access limit
    document、location、body 属性方法
    ASP对Excel的基本操作
    IE6/IE7/FF(火狐Firefox)及其他浏览器的兼容性通用解决方法
    CSS兼容IE与Firefox要点分析
    PHP函数
    vs2005 修改新增文件时的默认编码方式(转)
  • 原文地址:https://www.cnblogs.com/bear831204/p/2528484.html
Copyright © 2020-2023  润新知