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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM