WPF MVVM從入門到精通4:命令和事件

原文: WPF MVVM從入門到精通4:命令和事件

 

WPF MVVM從入門到精通1:MVVM模式簡介html

WPF MVVM從入門到精通2:實現一個登陸窗口前端

WPF MVVM從入門到精通3:數據綁定express

WPF MVVM從入門到精通4:命令和事件後端

WPF MVVM從入門到精通5:PasswordBox的綁定ide

WPF MVVM從入門到精通6:RadioButton等一對多控件的綁定this

WPF MVVM從入門到精通7:關閉窗口和打開新窗口spa

WPF MVVM從入門到精通8:數據驗證.net

完整示例代碼下載LoginDemocode

 

這一部分咱們要作的事情,是把點擊登陸按鈕的事件也在ViewModel裏實現。若不是用MVVM模式,可能XAML文件裏是這樣的:xml

<Button Grid.Row="3" Grid.ColumnSpan="2" Content="登陸" Width="200" Height="30" Click="Button_Click"/>

而跟XAML文件相關的CS文件裏則是這樣的:

private void Button_Click(object sender, RoutedEventArgs e)
{
    //業務處理邏輯代碼
}

如此一來,前端和後端的代碼又耦合在一塊兒了。其實,命令和事件都是能夠綁定的,就像數據同樣。

咱們先來了解一下命令。ICommand是全部命令的接口,它主要完成兩件事情,這個命令可否被執行,以及執行命令。

event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter);
public void Execute(object parameter);

例如當用戶名爲空時,咱們可能會禁用按鈕。當登陸按鈕跟一個命令綁定在一塊兒時,CanExecute會不斷被執行,若是返回false,按鈕的IsEnabled屬性也會被置爲false。

通常狀況下,咱們須要繼承ICommand接口來進行開發。

using System;
using System.Windows.Input;

namespace LoginDemo.ViewModel.Common
{
    /// <summary>
    /// 命令基類
    /// </summary>
    public class BaseCommand : ICommand
    {
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null)
                {
                    CommandManager.RequerySuggested += value;
                }
            }
            remove
            {
                if (_canExecute != null)
                {
                    CommandManager.RequerySuggested -= value;
                }
            }
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }
            return _canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            if (_execute != null && CanExecute(parameter))
            {
                _execute(parameter);
            }
        }

        private Func<object, bool> _canExecute;
        private Action<object> _execute;

        public BaseCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public BaseCommand(Action<object> execute) :
            this(execute, null)
        {
        }
    }
}

BaseCommand的功能很簡單,就是執行命令前先判斷一下命令能不能執行。

而後咱們就能夠綁定命令了,在後端這樣寫:

private BaseCommand clickLoginCommand;
public BaseCommand ClickLoginCommand
{
    get
    {
        if(clickLoginCommand==null)
        {
            clickLoginCommand = new BaseCommand(new Action<object>(o =>
            {
                //執行登陸邏輯
            }));
        }
        return clickLoginCommand;
    }
}

前端這樣寫:

<Button Grid.Row="3" Grid.ColumnSpan="2" Content="登陸" Width="200" Height="30" Command="{Binding ClickLoginCommand}"/>

點擊按鈕執行登陸邏輯的代碼就這樣完成了。但不要急着複製代碼,由於咱們不打算使用命令。

咱們知道,對於按鈕的操做,不必定是點擊,多是鼠標劃過,多是鼠標右擊。那Command觸發的是什麼呢?就是點擊,沒有其餘了。對於其餘控件,例如是輸入框,Command又表明什麼呢?文本改變事件能用Command嗎?這些問題讓咱們感到困惑,因此通常在項目中,我都只會使用事件,而不會使用命令(即便是單擊事件)。

BaseCommand這個類還能夠留着,咱們後面還須要使用的。在引入事件以前,咱們須要先引用一個dll:System.Windows.Interactivity.dll。這個dll並非.NET Framework的標配,它是Blend的一個類庫。能夠在擴展的程序集裏找到:

若是沒有找到(我安裝VS2017後就沒有找到),須要安裝如下庫纔有:

好了,引用了System.Windows.Interactivity.dll後,咱們就能夠開始講事件了。

有些事件是有參數的,例如鼠標移動這個事件,會帶上鼠標的位置。但咱們以前使用的命令,默認傳入的參數是null。爲了可以傳遞參數,咱們須要先定義一個事件基類:

using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace LoginDemo.ViewModel.Common
{
    /// <summary>
    /// 事件命令
    /// </summary>
    public class EventCommand : TriggerAction<DependencyObject>
    {
        protected override void Invoke(object parameter)
        {
            if (CommandParameter != null)
            {
                parameter = CommandParameter;
            }
            if (Command != null)
            {
                Command.Execute(parameter);
            }
        }

        /// <summary>
        /// 事件
        /// </summary>
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommand), new PropertyMetadata(null));

        /// <summary>
        /// 事件參數,若是爲空,將自動傳入事件的真實參數
        /// </summary>
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(null));
    }
}

如今,咱們能夠在ViewModel裏增長以下代碼:

private BaseCommand loginClick;
/// <summary>
/// 登陸事件
/// </summary>
public BaseCommand LoginClick
{
    get
    {
        if(loginClick==null)
        {
            loginClick = new BaseCommand(new Action<object>(o =>
            {
                //執行登陸邏輯
            }));
        }
        return loginClick;
    }            
}

而後在XAML文件裏,先加入i這個命名空間:xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity",而後修改按鈕的代碼:

<Button Grid.Row="3" Grid.ColumnSpan="2" Content="登陸" Width="200" Height="30">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <c:EventCommand Command="{Binding LoginClick}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

上面的代碼指出,Click這個事件,綁定到了LoginClick這個屬性。當咱們點擊按鈕的時候,LoginClick裏面的Action就會被執行。

相關文章
相關標籤/搜索