要開始使用命令,必須做三件事:
一:定義一個命令
二:定義命令的實現
三:為命令創建一個觸發器
WPF中命令系統的基礎是一個相對簡單的ICommand的接口,代碼如下:
public interface ICommand { event EventHandler CanExecuteChanged; bool CanExecute(object parameter); void Execute(object parameter); }
CanExecute用於確定命令是否處於可執行的狀態。典型的,UI控件能使用CanExecute來啟用或禁用 自己。也就是說,在相關的命令從CanExecute中返回False的時候,按鈕將變得不可用。
Execute是命 令的關鍵,當被調用時,它將觸發命令的執行。
要定義一個新命令,可以實現ICommand接口。如希望ICommand在被調用後關閉應用程序,代碼如下:
public class Exit : ICommand { event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { return true; } public void Execute(object parameter) { Application.Current.Shutdown(); } }
要把一個菜單項綁定到應用程序關閉這個命令上,可以把他們的Command屬性掛到Exit命令上,代 碼如下:
<MenuItem Header="_File"> <MenuItem Header="_Exit"> <MenuItem.Command> <local:Exit/> </MenuItem.Command> </MenuItem> </MenuItem>
由於把命令用於多個位置比較常見,所以創建一個存儲命令的靜態字段也常見:
public static readonly ICommand ExitCommand = new Exit();
這樣做的好處是,通過 這個類型為ICommand的字段,可以讓Exit命令的實現完全私有化。現在,可以把Exit標記為私有類,並把標記 轉化為綁定到靜態字段,代碼如下:
<MenuItem Header="_File"> <MenuItem Header="_Exit" Command="{x:Static local:WinCommand.ExitCommand}"/> </MenuItem>
下面我們通過添加一個和Close命令掛接的按鈕,可以為窗口編寫一個 模板,以實現關閉窗口的功能,代碼如下:
<Window.Style> <Style TargetType="Window"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Window"> <DockPanel> <StatusBar DockPanel.Dock="Bottom"> <StatusBarItem> <Button Command="{x:Static ApplicationCommands.Close}">Close</Button> </StatusBarItem> </StatusBar> <ContentPresenter/> </DockPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Style>
我們接著要通過把命令綁定添加到窗口中讓窗口關閉。
/// <summary> /// WinCommand.xaml 的交互邏輯 /// </summary> public partial class WinCommand : Window { public static readonly ICommand ExitCommand = new Exit(); public WinCommand() { InitializeComponent(); CommandBindings.Add( new CommandBinding( ApplicationCommands.Close, CloseExecuted)); } void CloseExecuted(object sender, ExecutedRoutedEventArgs e) { this.Close(); } }
使用命令可以清晰地把顯示和行為分開。通過使用單一的名稱為所需的語義動作簽名,在嘗試 把多個控件和單個事件處理過程掛接起來的時候,可以避免很多由此引發的緊耦合問題。通常,應用程序邏輯 應該總是通過命令的方式來實現的,而不是事件處理程序。對於很多需要直接掛接到事件處理過程上的常見例 子,用觸發器來處理更好。
命令與數據綁定
使用命令的一個令人振奮和強大的特性 就是和 數據綁定集成。由於Command和CommandParameter都是元素上的屬性,所以他們都能被設置為一些綁定到他們 的數據。因此,可以使用綁定的數據內容來確定應該發生的動作。
為了演示他們是如何融合到一起的,將以C:\下面的文件的應用程序來開頭。首先,定義一個顯示內 容的ListBox,和一個顯示了每個文件名的數據模板,代碼如下:
<ListBoxMargin="2"Name="lbFile">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlockText="{Binding Path=Name} "/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox&g t;
在後台,把ItemSource屬性設置為文件列表:
public WinCommandAndBinding() { InitializeComponent(); FileInfo[] fileList = new DirectoryInfo("C:\\").GetFiles("*.*"); lbFile.ItemsSource = fileList; }
運行如下:
現在,再添加一個按鈕用來顯 示文件,但不希望任何文件都被打開。所以,要在加載的文件上提供某種類型的過濾器。現實現兩個命令Open 和Blocked並為他們提供某種類型的處理過程,代碼如下:
public static readonly RoutedCommand OpenCommand = new RoutedCommand("Open", typeof(WinCommandAndBinding)); public static readonly RoutedCommand BlockedCommand = new RoutedCommand("Blocked", typeof(WinCommandAndBinding)); public WinCommandAndBinding() { InitializeComponent(); CommandBindings.Add(new CommandBinding (OpenCommand, delegate(object sender,ExecutedRoutedEventArgs e){ Process.Start("notepad.exe",(string)e.Parameter);})); CommandBindings.Add(new CommandBinding(BlockedCommand, delegate(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show((string)e.Parameter, "Blocked"); })); FileInfo[] fileList = new DirectoryInfo("C:\\").GetFiles("*.*"); lbFile.ItemsSource = fileList; } }
在定義好兩個命令後,就可以更新文件的數據模板來包含按鈕了。在命令參數(文件名)中使用數據 綁定。對應命令本身,由於希望某些條目用OpenCommand,而其他條目用BlockedCommand,所以將使用 IValueConvert把文件名轉換為ICommand,代碼如下:
<ListBox Margin="2" Name="lbFile"> <ListBox.ItemTemplate> <DataTemplate> <WrapPanel> <TextBlock Text="{Binding Path=Name}"/> <Button Margin="5" CommandParameter="{Binding Path=FullName}"> <Button.Command> <Binding> <Binding.Converter> <local:FileToCommandConverter/> </Binding.Converter> </Binding> </Button.Command> Show </Button> </WrapPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
下面是轉換器:
public class FileToCommandConverter : IValueConverter { public object Convert(object value ,Type targetType,object parameter,CultureInfo culture) { string ext = ((FileInfo)value).Extension.ToLowerInvariant(); if (ext == ".txt") return WinCommandAndBinding.OpenCommand; else return WinCommandAndBinding.BlockedCommand; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
運行結果:
這個例子雖然有點微不足道, 不過可以使用CanExecute方法輕松地完成類似的行為,並針對“壞”文件禁用這個命令。然而,這裡最重要的 一點是,可以返回任何命令。可以使用任何基於數據的邏輯來確定任何元素的行為。
另外我們可以考 慮下能不能用數據觸發器實現呢?呵呵,可以的,這等於把命令、數據綁定和觸發器三者融合到一起了?是不 是很強大,呵呵下面是代碼:
<ListBox Margin="2" Name="lbFile2"> <ListBox.ItemTemplate> <DataTemplate> <WrapPanel> <TextBlock Text="{Binding Path=Name}"/> <Button x:Name="btnShow" Margin="5" CommandParameter="{Binding Path=FullName}" Command="{x:Static local:WinCommandAndBinding.BlockedCommand}" Content=" Block"/> </WrapPanel> <DataTemplate.Triggers> <DataTrigger Value=".txt"> <DataTrigger.Binding> <Binding Path='Extension'> <Binding.Converter> <local:ToLowerInvariantConvert/> </Binding.Converter> </Binding> </DataTrigger.Binding> <Setter TargetName="btnShow" Property="Command" Value="{x:Static local:WinCommandAndBinding.OpenCommand}"/> <Setter TargetName="btnShow" Property="Content" Value="Show"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
轉換器:
public class ToLowerInvariantConvert : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return ((string)value).ToLowerInvariant(); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }