• WPF入门(3)——命令


    命令是ICommand类型的属性,binding到控件上,用于代替事件,个人认为事件也很好,命令只是轻度解耦前后端。
    闲话少说,上代码,示例是ScreenToGif的源代码中的一个命令:

    public ICommand OpenOptions
            {
                get
                {
                    return new RelayCommand
                    {
                        CanExecutePredicate = a => true, //TODO: Always let this window opens or check if there's any other recorder active?
                        ExecuteAction = a =>
                        {
                            var options = Application.Current.Windows.OfType<Options>().FirstOrDefault();
                            var tab = a as int? ?? 0; //Parameter that selects which tab to be displayed.
    
                            if (options == null)
                            {
                                options = new Options(tab);
                                options.Closed += (sender, args) =>
                                {
                                    CloseOrNot();
                                };
    
                                //TODO: Open as dialog or not? Block other windows?
                                options.Show();
                            }
                            else
                            {
                                if (options.WindowState == WindowState.Minimized)
                                    options.WindowState = WindowState.Normal;
    
                                options.SelectTab(tab);
                                options.Activate();
                            }
                        }
                    };
                }
            }
    

    这样看有些麻烦,我们省略一些暂时对理解不重要的东西:

    public ICommand OpenOptions
            {
                get
                {
                    return new RelayCommand
                    {
                        CanExecutePredicate = a => true, 
                        ExecuteAction = a =>
                        {
                            //这是个lambda表达式
                        }
                    };
                }
            }
    

    实际上就是返回了一个new RelayCommand,这个RelayCommand是作者自定义的一个路由命令的类,代码如下:

        /// <summary>
        /// 路由命令
        /// </summary>
        internal class RelayCommand : ICommand
        {
            /// <summary>
            /// 作者自定义的字段 
            /// </summary>
            public Predicate<object> CanExecutePredicate { get; set; }
            /// <summary>
            /// 作者自定义的字段 
            /// </summary>
            public Action<object> ExecuteAction { get; set; }
    
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="canExecute"></param>
            /// <param name="execute"></param>
            public RelayCommand(Predicate<object> canExecute, Action<object> execute)
            {
                CanExecutePredicate = canExecute;
                ExecuteAction = execute;
            }
            /// <summary>
            /// ICommand字段
            /// </summary>
            public RelayCommand()
            { }
            /// <summary>
            /// ICommand字段
            /// </summary>
            public event EventHandler CanExecuteChanged
            {
                add => CommandManager.RequerySuggested += value;
                remove => CommandManager.RequerySuggested -= value;
            }
            /// <summary>
            /// ICommad字段
            /// </summary>
            /// <param name="parameter"></param>
            /// <returns></returns>
            public bool CanExecute(object parameter)
            {
                return CanExecutePredicate == null || CanExecutePredicate(parameter);
            }
            /// <summary>
            /// ICommand字段
            /// </summary>
            /// <param name="parameter"></param>
            public void Execute(object parameter)
            {
                ExecuteAction(parameter);
            }
        }
    

    使用路由的好处就是不管啥命令返回的都是RelayCommand类的实例,只要给该实例绑定上相应的方法就好了,方法可以随意写,上面作者就是为路由实例赋值了一个lambda表达式。

    下面使用实例操作一下,下面的例子按下按钮之后修改button的context,为了方便我就直接使用上面ScreenToGif的RelayCommand。为了不与之前的代码冲突,我新加一个button:

    新加的button的context同样是binding到Name属性,Command则是binding到了名为“ChangeName”命令上,该命令如下:

    此时,MainViewModel.cs的所有代码如下:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;
    
    namespace WpfApp.ViewModel
    {
        class MainViewModel : INotifyPropertyChanged
        {
            #region INPC
            public event PropertyChangedEventHandler PropertyChanged;
            public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            #endregion
    
    
            private string name;
            public string Name
            {
                get { return name; }
                set
                {
                    name = value;
                    OnPropertyChanged("Name");
                }
            }
    
            public ICommand ChangeName
            {
                get
                {
                    return new RelayCommand() {
                        CanExecutePredicate = a => true,
                        ExecuteAction = a =>
                        {
                            Name = "ChangeName Command";
                        }
                    };
                }
            }
        }
    
        /// <summary>
        /// 路由命令
        /// </summary>
        internal class RelayCommand : ICommand
        {
            public Predicate<object> CanExecutePredicate { get; set; }
            public Action<object> ExecuteAction { get; set; }
    
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="canExecute"></param>
            /// <param name="execute"></param>
            public RelayCommand(Predicate<object> canExecute, Action<object> execute)
            {
                CanExecutePredicate = canExecute;
                ExecuteAction = execute;
            }
            /// <summary>
            /// ICommand字段
            /// </summary>
            public RelayCommand()
            { }
            /// <summary>
            /// ICommand字段
            /// </summary>
            public event EventHandler CanExecuteChanged
            {
                add => CommandManager.RequerySuggested += value;
                remove => CommandManager.RequerySuggested -= value;
            }
            /// <summary>
            /// ICommad字段
            /// </summary>
            /// <param name="parameter"></param>
            /// <returns></returns>
            public bool CanExecute(object parameter)
            {
                return CanExecutePredicate == null || CanExecutePredicate(parameter);
            }
            /// <summary>
            /// ICommand字段
            /// </summary>
            /// <param name="parameter"></param>
            public void Execute(object parameter)
            {
                ExecuteAction(parameter);
            }
        }
    }
    
    

    按照我的设定,按下第一个按钮会触发Button_Click方法(该方法在view的后台代码中,也就是我的工程中的MainWindow.xaml.cs文件)这是事件的方法,按下后会更改Name属性的内容,所有binding到该属性的控件内容都会跟随更改。
    按下第二个按钮,则是触发ChangeName命令,该命令则是view的DataContext(也就是MainViewModel类)的ChangeName属性。按下后同样会修改Name属性的内容,所有所有binding到该属性的控件内容都会跟随更改。
    下面debug一下:

    喜大普奔,没有啥bug,哈哈哈哈哈哈

    工程源代码上传在GitHub上了:https://github.com/feipeng8848/WPF-Demo

    参考:https://www.codeproject.com/Articles/1052346/ICommand-Interface-in-WPF

  • 相关阅读:
    Android中fragment之间和Activity的传值、切换
    IOS后台运行浅析
    IOS7 Background Fetch后台应用程序刷新
    IOS 7四种后台机制
    注册苹果开发者账号的详细步骤
    iOS关闭键盘的两种简单方法
    iOS5 切换中文键盘时覆盖输入框的完美解决方案
    IOS开发之NSLog使用技巧
    IOS总结 静变量static、全局变量extern、局部变量、实例变量
    高德地图初始化 ios
  • 原文地址:https://www.cnblogs.com/feipeng8848/p/11640306.html
Copyright © 2020-2023  润新知