在實際的編程工作中,我們可以只是用事件,不用命令,程序的邏輯也一樣被驅動的很好,但我們不能阻止程序員按照自己的習慣去寫代碼。比如保存事件的處理器,程序員們可以寫Save()、Savehandler()、SaveDocument()...這些都符合代碼規范,但是遲早有一天整個項目會變得無法讀懂,新來的程序員或者修改bug的程序員會很抓狂。
WPF命令簡介
WPF的命令是實現了ICommand接口的類。ICommand接口非常簡單,只包含兩個方法和一個事件。
- Execute方法:命令執行,或者說命令作用於目標之上。
- CanExecute方法:在執行之前來彈指命令是否可被執行。
- CanExecuteChanged事件:當命令執行狀態發生改變時,可激發此事件來通知其他對象。
RoutedCommand就是這樣一個實現了ICommand接口的類。RoutedCommand在實現ICommand接口時,並未向其中添加任何邏輯。
WPF的命令系統由幾個基本元素構成,他們是:
- 命令:WPF命令實際上就是實現了ICommand接口的類,平時使用最多的是RoutedCommand類。
- 命令源:即命令的發送者,是實現了ICommandSource接口的類。
- 命令目標:即命令將推送給誰,或者說命令將作用在誰身上。命令目標是必須實現了IInputElement接口的類。
- 命令關聯:負責把一些外圍邏輯與命令關聯起來,比如執行之前對命令是否可執行進行判斷、命令執行后還有那些后續工作等。
命令具有一處聲明處處可用的特點,因此,MS在WPF類庫里准備而來一些便捷的命令庫,例如:
下面分別用WPF命令庫,和自定義命令實現同樣的功能。
WPF命令庫命令
Demo程序的UI如下:
<Window x:Class="WPFCommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Window.CommandBindings> <CommandBinding Command="New" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/> </Window.CommandBindings> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <DockPanel Height="60" Grid.Row="0"> <TextBlock VerticalAlignment="Center" Text="Name:" DockPanel.Dock="Left" Width="100" FontSize="30" Foreground="PaleVioletRed"/> <TextBox x:Name="txtName" DockPanel.Dock="Right" FontSize="40"/> </DockPanel> <Button Command="New" CommandParameter="Teacher" Content="New Teacher" Grid.Row ="1"/> <Button Command="New" CommandParameter="Student" Content="New Student" Grid.Row ="2"/> <ListBox x:Name="listBox1" Grid.Row="3" Grid.RowSpan="2"/> </Grid> </Window>
后台代碼如下:
using System.Windows; using System.Windows.Input; namespace WPFCommand { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { if (string.IsNullOrWhiteSpace(txtName.Text)) { e.CanExecute = false; } else { e.CanExecute = true; } } private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { string name = txtName.Text; if (e.Parameter.ToString() == "Teacher") { listBox1.Items.Add("New Teacher: "+name); } if (e.Parameter.ToString() == "Student") { listBox1.Items.Add("New Student: "+name); } } } }
程序的運行結果如下:
當txtName.Text為WhiteSpace時,命令不可被執行。
執行后效果如下:
前面說過不用命令,程序的邏輯也可以被驅動的很好。且在一般情況下,在程序中使用與邏輯無關的RoutedCommand來跑跑龍套也就足夠了,上面這個命令用的就有十足的“醬油”的嫌疑。
OK,把打醬油進行到底吧,下面自定義一個命令,實現相同的功能。
WPF自定義命令
注意對比前面使用命令庫命令的方法異同。
程序的UI如下:
<Window x:Class="MyCommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="My Command Window" Height="350" Width="525" x:Name="mainWindow" > <Grid > <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <DockPanel Height="60" Grid.Row="0"> <TextBlock VerticalAlignment="Center" Text="Name:" DockPanel.Dock="Left" Width="100" FontSize="30" Foreground="PaleVioletRed"/> <TextBox x:Name="txtName" DockPanel.Dock="Right" FontSize="40"/> </DockPanel> <Button CommandParameter="Teacher" x:Name="button1" Content="New Teacher" Grid.Row ="1"/> <Button CommandParameter="Student" x:Name="button2" Content="New Student" Grid.Row ="2"/> <ListBox x:Name="listBox1" Grid.Row="3" Grid.RowSpan="2"/> </Grid> </Window>
后台代碼如下:
using System.Windows; using System.Windows.Input; namespace MyCommand { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // InitCmd(); } //聲明並定義命令 private RoutedCommand newCmd=new RoutedCommand ("NewTS",typeof(MainWindow )); private void InitCmd() { //創建命令關聯 CommandBinding cb = new CommandBinding(); cb.Command = newCmd; cb.CanExecute += new CanExecuteRoutedEventHandler(CanExecute1); cb.Executed += new ExecutedRoutedEventHandler(Executed1); //把命令關聯安置在外圍控件上 mainWindow.CommandBindings.Add(cb); //把命令賦值給命令源(發送者) button1.Command = newCmd; button2.Command = newCmd; } private void CanExecute1(object sender,CanExecuteRoutedEventArgs e) { //e.CanExecute = true; if (string.IsNullOrWhiteSpace(txtName.Text)) { e.CanExecute = false; } else { e.CanExecute = true; } } private void Executed1(object sender, ExecutedRoutedEventArgs e) { string name = txtName.Text; if (e.Parameter.ToString() == "Teacher") { listBox1.Items.Add("New Teacher: " + name); } if (e.Parameter.ToString() == "Student") { listBox1.Items.Add("New Student: " + name); } } } }
程序的運行效果和上面的相同:
當txtName.Text為WhiteSpace時,命令不可被執行。
執行后效果如下: