WPF使用MVVM(二)-命令綁定
上一節已經介紹了WPF的屬性綁定,這使得我們只需要指定界面的DataContext,然后就可以讓界面綁定我們的屬性數據呢。
但是上一節還遺留了一個問題就是我們的按鈕的Click方法,依然是寫在界面的后台中的,現在我們希望將按鈕的Click方法也采用綁定的形式。
原先是這樣的:
<Button
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="20"
Click="Button_Click"
Content="更新一下信息"
FontSize="30"
FontWeight="Bold" />
希望變成這樣:
<Button
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="20"
Click="{Binding ClickAction}"
Content="更新一下信息"
FontSize="30"
FontWeight="Bold" />
讓我們的MainWindowVM(ViewModel)也提供一個方法,讓我們綁定一下,這樣界面的數據和按鈕的點擊處理邏輯,都放到了橋梁ViewModel中了,界面看起來也很清爽。
Command
WPF呢,為了讓我們用MVVM的形式替換按鈕的點擊行為,給我們提供了一個Command的屬性,讓我們也可以像綁定屬性的方式,來綁定我們的點擊方法,具體的寫法如下:
<Button
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="20"
Command="{Binding ClickAction}"
Content="更新一下信息"
FontSize="30"
FontWeight="Bold" />
之前寫的Button_Click方法也可以直接刪除了。
注意:Command屬性僅僅作為Click行為的綁定,其他行為,如鼠標移入、移出。。。等行為,要使用另外的MVVM方式進行綁定。(本文只介紹點擊行為,后續介紹其他行為的MVVM實現)
添加ClickAction的實現
上面我們也刪除了Button_Click方法,並且還給Button按鈕的Command屬性綁定了一個方法叫做ClickAction,接下來我們就要在MainWindowVM(ViewModel)中去添加這個方法。
要實現綁定的方法ClickAction,就需要用到ICommand接口,需要我們自己創建類型去實現接口的CanExecute、Execute、CanExecuteChanged,下面直接貼一下實現接口的代碼,需要新建一個類,名字我們取RelayCommand:
public class RelayCommand : ICommand
{
/// <summary>
/// 命令能否執行
/// </summary>
readonly Func<bool> _canExecute;
/// <summary>
/// 命令執行的方法
/// </summary>
readonly Action _execute;
/// <summary>
/// 命令的構造函數
/// </summary>
/// <param name="action">命令需執行的方法</param>
/// <param name="canExecute">命令是否可以執行的方法</param>
public RelayCommand(Action action, Func<bool> canExecute)
{
_execute = action;
_canExecute = canExecute;
}
/// <summary>
/// 判斷命令是否可以執行
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(Object parameter)
{
if (_canExecute == null)
return true;
return _canExecute();
}
/// <summary>
/// 執行命令
/// </summary>
/// <param name="parameter"></param>
public void Execute(Object parameter)
{
_execute();
}
/// <summary>
/// 事件追加、移除
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
}
創建這個類,就是為了在使用命令的時候, 創建一條命令出來用於綁定,這個類型接收兩個參數,一個是命令執行的方法,另一個是有返回值的方法, 這個返回值bool用來確定,該條命令是否可以執行,如果命令不能被執行,則按鈕的IsEnabled就被會設置成不可點擊,下面我們來挨個看下效果
MainWindowVM中創建一個命令
剛才我們已經做好了創建命令的准備工作,下面直接創建一個命令,並給這個命令指定一個方法即可。
在MainWindowVM添加如下代碼:
/// <summary>
/// 命令要執行的方法
/// </summary>
void UpdateNameExecute()
{
EmployeeM.Name = "王明(原屬性修改)";
EmployeeM = EmployeeM;
}
/// <summary>
/// 命令是否可以執行
/// </summary>
/// <returns></returns>
bool CanUpdateNameExecute()
{
return true;
}
/// <summary>
/// 創建新命令
/// </summary>
public ICommand ClickAction
{
get
{
return new RelayCommand(UpdateNameExecute, CanUpdateNameExecute);
}
}
注意,創建這個新的命令的名字需要和我們界面按鈕Command中綁定的名字一致,叫ClickAction。
這時候我們運行一下程序,點擊按鈕,可以看到命令是可以生效的。
此時我們做一個小小的改動,我們將是否可以執行的方法返回為False:
/// <summary>
/// 命令是否可以執行
/// </summary>
/// <returns></returns>
bool CanUpdateNameExecute()
{
return false;
}
再次運行能夠看到,界面中按鈕已經是不可點擊的狀態了!
所以我們綁定的這個命令是否可以執行,是直接影響到按鈕能否被點擊的!這個值會直接作用在按鈕的IsEnabled上。
命令帶點私貨-參數
上面的命令就是純命令,啥參數都沒帶上,有時候希望執行命令的時候,希望能夠傳個參數,那就需要改造一下了!
采用泛型的形式,給Action加點料,重新貼一下RelayCommand的代碼:
public class RelayCommand<T> : ICommand
{
/// <summary>
/// 命令能否執行
/// </summary>
readonly Func<bool> _canExecute;
/// <summary>
/// 命令執行的方法
/// </summary>
readonly Action<T> _execute;
/// <summary>
/// 命令的構造函數
/// </summary>
/// <param name="action">命令需執行的方法</param>
/// <param name="canExecute">命令是否可以執行的方法</param>
public RelayCommand(Action<T> action, Func<bool> canExecute)
{
_execute = action;
_canExecute = canExecute;
}
/// <summary>
/// 判斷命令是否可以執行
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(Object parameter)
{
if (_canExecute == null)
return true;
return _canExecute();
}
/// <summary>
/// 執行命令
/// </summary>
/// <param name="parameter"></param>
public void Execute(Object parameter)
{
_execute((T)parameter);
}
/// <summary>
/// 事件追加、移除
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
}
在MainWindowVM(ViewModel)中創建的命令和給到的方法也要有點小變化:
/// <summary>
/// 命令要執行的方法
/// </summary>
void UpdateNameExecute(object sender)
{
EmployeeM.Name = "王明(原屬性修改)";
EmployeeM = EmployeeM;
}
/// <summary>
/// 命令是否可以執行
/// </summary>
/// <returns></returns>
bool CanUpdateNameExecute()
{
return true;
}
/// <summary>
/// 創建新命令
/// </summary>
public ICommand ClickAction
{
get
{
return new RelayCommand<object>(UpdateNameExecute, CanUpdateNameExecute);
}
}
參數從哪里傳呢, 當然是我們的界面傳了,通過按鈕的CommandParameter屬性來傳,這里我們將按鈕自己傳過去!
<Button
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="20"
Command="{Binding ClickAction}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}"
Content="更新一下信息"
FontSize="30"
FontWeight="Bold" />
運行斷點看一下,能夠看到按鈕自身已經當作參數傳入了:
下一節說一下事件的綁定,讓其他事件,如MouseEnter、MouseLeave也能夠像按鈕的Command一樣。