[WPF] MVVM 軟體架構模式 - 透過 DelegateCommand 綁定方法

透過 DelegateCommand 綁定方法

先前文章綁定的方法都是透過綁定具 Command 屬性的來源,包括 MenuItem, ButtonKeyGesture

如果想綁定不是 Command 的事件,顯然就不能用 RelayCommand 的辦法!比如 Page 的 PeLoad 或 Form 的 Load

這裡用另一個辦法來解決,使用 System.Windows.Interactivity 來 Trigger 事件,Interactivity 是 Expression Blend 的附屬功能

首先要安裝這套件,我選擇用 NuGet 來安裝

也可使用 Visual Studio Installer 安裝這個附加功能,或是有保留參考 DLL 也可以用加入參考啟用功能

先把基本的 DelegateCommand 建立起來

using System;
using System.Diagnostics;
using System.Windows.Input;

namespace MVVM
{
    public class DelegateCommand : ICommand
    {
        private readonly Func<object, bool> _canExecute;
        private readonly Action<object> _execute;
        bool canExecuteCache;

        // 建構子(多型)
        public DelegateCommand(Action<object> execute, object canExecute) : this(execute, null)
        {
        }
        // 建構子(傳入參數)
        public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            // 簡化寫法 if(execute == null) throw new ArgumentNullException("execute");
            this._execute = execute ?? throw new ArgumentNullException("execute");
            this._canExecute = canExecute;
        }

        #region -- ICommand Members --
        // 在XAML使用Interaction繫結這個事件
        public event EventHandler CanExecuteChanged;

        // 下面兩個方法是提供給 View 使用的
        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            bool temp = _canExecute(parameter);

            if (canExecuteCache != temp)
            {
                canExecuteCache = temp;
                if (CanExecuteChanged != null)
                {
                    CanExecuteChanged(this, new EventArgs());
                }
            }
            return canExecuteCache;
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }
        #endregion
    }
}

這次範例也是用 Button 來示範,在 ViewModel 裡加上以下程式碼,建構式內要初始化對應的 DelegateCommand 類別方法

public class TestViewModels : ViewModelBase  // ViewModelBase繼承INotifyPropertyChanged介面
{
    public TestViewModels()
    {
        //初始化DelegateCommand
        cm1click = new DelegateCommand(cm1Click, CanExecute);
    }

    private bool CanExecute(object param)
    {
        return true;  // 假設都執行
    }

    // DelegateCommand
    #region command1
    public ICommand cm1click { get; set; }
    private void cm1Click(object param)
    {
        MessageBox.Show("CM1 clicked!");
    }
    #endregion command1
}

對應的 View 的 xaml 部分加入命名空間 - interactivity

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

或是以下方式也是一樣引入 interactivity 的命名空間

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

使用 Interactivity 主要是透過 <i:Interaction.Trigger> 標記想觸發的事件到事件觸發器  <i:EventTrigger> 內,並透過 <i:InvokeCommandAction> 綁定想執行的命令/方法

xaml 再加入以下部分

<Button x:Name="BTN_CM1" Content="DelegateCommand">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName = "Click">
            <i:InvokeCommandAction Command = "{Binding cm1click}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

對照之前的 RelayCommand 的範例來看,DelegateCommand 的 xaml 部分更直覺了!

想修改綁定給其事件,也只要變更 EventName 就完成綁定修改!

而且所有事件都可以綁定,這是更大的優勢部分!

但簡單應用 RelayCommand 還是很方便使用的,就看用途來決定用哪種方法囉~