這節我們介紹一下WPF中比較重要的接口ICommand,也是WPF中一個新的特性,做過WinForm朋友都知道,WinForm開發是基于事件驅動開發模式,比如一個Button有Click事件,當我點擊該按鈕時,在當前頁面會執行具體的業務,這樣帶來的UI和業務層產生緊密的耦合,WPF也繼承了WinForm這一舊的開發模式,同時給我們添加了新的方法,使得UI和后端業務完全隔離,從而UI和業務邏輯解耦,我們來看一下該接口的定義:
public interface ICommand{event?EventHandler?CanExecuteChanged;bool?CanExecute(object?parameter);void Execute(object parameter);}
在整個MVVM架構中該接口起著非常重要的作用,我們來看一下該接口成員,CanExecuteChanged事件觸發通知UI界面做出響應,比如按鈕禁用或啟用,表示CanExecute該接口返回一個bool值,表示是否執行命令,返回true,命令執行,false命令不執行。
我們通過一個簡單的例子實現命令綁定,需求比較簡單:我們定義一個輸入框,如果為空狀態,提交按鈕禁用,如果有值,按鈕啟用,點擊提交,并將值顯示到頁面。
我們定義一個DelegateCommand 實現ICommand接口:
public class DelegateCommand : ICommand{private Action _execute;private?Func<bool>?_canExecute;public DelegateCommand(Action executeMethod){_execute = executeMethod;}public DelegateCommand(Action executeMethod, Func<bool> canExecute): this(executeMethod){this._canExecute = canExecute;}public bool CanExecute(object parameter){return _canExecute();}public event EventHandler CanExecuteChanged {add{CommandManager.RequerySuggested += value;}remove{CommandManager.RequerySuggested -= value;}}public void Execute(object parameter){_execute();}}
接下來我們定義一個ViewMode類,命名為SampleViewModel實現INotifyPropertyChanged接口
public class SampleViewModel : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;private void NotifyPropertyChanged([CallerMemberName] String propertyName = ""){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}private string name = String.Empty;public string Name{get{return name;}set{if (value != this.name){this.name = value;NotifyPropertyChanged();}}}private string displayName = string.Empty;public string DisplayName{get { return displayName; }set{if (value != this.displayName){displayName = value;NotifyPropertyChanged();}}}ICommand delegateCommand;public ICommand DelegateCommand{get{if (delegateCommand == null){delegateCommand = new DelegateCommand(Execute, CanExecute);}return delegateCommand;}}public void Execute(){DisplayName = Name;}public bool CanExecute(){return !string.IsNullOrEmpty(Name);}}
? XMLA頁面定義:
<Window x:Class="Example_21.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:Example_21"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Grid><Grid.RowDefinitions><RowDefinition></RowDefinition><RowDefinition></RowDefinition><RowDefinition></RowDefinition></Grid.RowDefinitions><TextBox Grid.Row="0" Name="txtBox" Width="200" Text="{Binding Path=Name,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Center"/><TextBlock Grid.Row="1" Width="200" Text="{Binding Path=DisplayName}"></TextBlock><Button Grid.Row="2" x:Uid="btnSend" x:Name="btnSend" FontSize="16" Content="提交" Width="100" Height="50" Margin="0,0,0,5" Command="{Binding DelegateCommand}" ></Button></Grid>
</Window>
MainWindow.xaml.cs 文件
public partial class MainWindow : Window{public MainWindow(){InitializeComponent();this.DataContext = new SampleViewModel();}}
從上面的例子可以看出我們可以使用控件的Command屬性來綁定要執行的命令,我們還可以設置CommandParameter來傳遞參數,可以把UI上數據傳遞到后臺,WPF整個框架是基于數據驅動,我們可以做到把UI層完全剝離出來,所以說WPF是一個前端和后臺可以完全分離的框架。其實走到這里結合我們WPF-18 INotifyPropertyChanged 接口我們有點看到了MVVM開發模式的影子。
接下來我們使用MVVM模式做一個簡單的Demo