• WPF之自定义委托命令


    常用命令

    WPF的命令实际上就是实现了ICommand接口的类,平时使用最多的是RoutedCommand类,还可以使用自定义命令。

    RoutedCommand只负责跑腿,并不对命名目标做任何操作,实际操作没那么方便而且需要在后台实现相关的事件,可以参考WPF 命令

    自定义命令直接在命令目标上起作用,而不像RoutedCommand那样先在命令目标上激发出路由事件等外围控件捕捉到事件后再“翻过头来”对命令目标加以处理

    委托命令

    实现一个DelegateCommand,代码如下:

    using System;
    using System.Windows.Input;
    
    /// <summary>
    /// 委托命令
    /// </summary>
    public class DelegateCommand : ICommand
    {
        private Action executeAction;
        private Func<bool> canExecuteFunc;
    
        /// <summary>
        /// 当出现影响是否应执行该命令的更改时发生。
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        /// <summary>
        /// 构造函数,不指定canExecute委托时CanExecute方法默认返回true
        /// </summary>
        /// <param name="execute"></param>
        /// <param name="canExecute"></param>
        public DelegateCommand(Action execute, Func<bool> canExecute = null)
        {
            if (execute != null)
            {
                executeAction = execute;
                canExecuteFunc = canExecute;
            }
    
        }
    
        /// <summary>
        /// 定义在调用此命令时要调用的方法。
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            if (executeAction != null && CanExecute(parameter))
            {
                executeAction();
            }
        }
    
        /// <summary>
        /// 定义确定此命令是否可在其当前状态下执行的方法。
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            if (canExecuteFunc == null)
            {
                return true;
            }
            return canExecuteFunc();
        }
    }
    

    上面的代码使用了系统的CommandManager.RequerySuggested如果ViewModel有继承绑定基类,可以在基类中监控属性值的变更并触发CanExecuteChanged以节省性能损耗。此时,添加如下代码:

    public void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }
    }
    

    委托命令(泛型)

    为需要传参数的委托命令实现一个泛型版本,相当于直接使用object,泛型可以在编译期间检查类型错误。DelegateCommand<T>代码如下:

    using System;
    using System.Windows.Input;
    
    /// <summary>
    /// 委托命令——带泛型参数
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DelegateCommand<T> : ICommand
    {
        private Action<T> executeAction;
        private Func<T, bool> canExecuteFunc;
    
        /// <summary>
        /// 当出现影响是否应执行该命令的更改时发生。
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        /// <summary>
        /// 构造函数,不指定canExecute委托时CanExecute方法默认返回true
        /// </summary>
        /// <param name="execute"></param>
        /// <param name="canExecute"></param>
        public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
        {
            if (execute != null)
            {
                executeAction = execute;
                canExecuteFunc = canExecute;
            }
        }
    
        /// <summary>
        /// 定义在调用此命令时要调用的方法。
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            if (executeAction != null && CanExecute(parameter))
            {
                executeAction(ChangeTo<T>(parameter));
            }
        }
    
        /// <summary>
        /// 定义确定此命令是否可在其当前状态下执行的方法。
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            if (canExecuteFunc == null)
            {
                return true;
            }
            return canExecuteFunc(ChangeTo<T>(parameter));
        }
    
        /// <summary>
        /// object转为泛型类型,兼容数值、对象实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        private static T ChangeTo<T>(object obj)
        {
            T result = default(T);
            if (obj != null)
            {
                try
                {
                    result = (T)Convert.ChangeType(obj, typeof(T));
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
            return result;
        }
    }
    

    泛型版本的类型转换比较麻烦,泛型参数需要考虑字符串、数值、对象实例等情况。参数尽量使用后台绑定的值,绑定更新过程可以将类型转换异常提前暴露出来,避免异常发生在命令执行过程中

    使用委托命令

    创建一个MainViewModel,代码如下:

    class MainViewModel
    {
        public bool CanExecute { get; set; }
    
        public int Param { get; set; }
    
        public DelegateCommand NormalCommand { get; }
    
        public DelegateCommand<int> ParamCommand { get; }
    
        public MainViewModel()
        {
            NormalCommand = new DelegateCommand(() => { MessageBox.Show("无参命令执行成功"); }, () => CanExecute);
    
            ParamCommand = new DelegateCommand<int>(i => { MessageBox.Show("参数命令执行成功:" + i); }, i => CanExecute);
        }
    }
    

    界面的XAML代码如下:

    <StackPanel>
        <CheckBox Content = "命令开关" IsChecked="{Binding CanExecute}"/>
        <Label Content = "命令参数:" />
        < TextBox Text="{Binding Param}"/>
        <Button Content = "无参数命令" Command="{Binding NormalCommand}"/>
        <Button Content = "有参数命令" Command="{Binding ParamCommand}" CommandParameter="{Binding Param}" />
    </StackPanel>
    

    在后台代码中添加DataContext:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
        }
    }
    

    运行程序,效果如下:

    参考资料

    MVVM模式解析和在WPF中的实现(三)命令绑定
    Prism.Core/Commands

  • 相关阅读:
    七七四十九劫,九九八十一难
    Android中用layer-list编写阴影效果
    Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
    Effective C++ 24,25
    windows系统port监听
    线程同步辅助类——Exchanger
    C/C++中constkeyword
    【COCOS2DX-游戏开发之三三】TMX边界控制与小窗体内预览TMX
    IDFA的值什么时候会发生改变
    hadoop(六)
  • 原文地址:https://www.cnblogs.com/timefiles/p/WpfDelegateCommand.html
Copyright © 2020-2023  润新知