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


免責聲明!

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



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