WPF 命令系統將「語義動作」與「觸發介面」解耦,支援多控件共用同一命令、自動啟用/禁用 UI、鍵盤捷徑關聯、MVVM 綁定。本文從 ICommand 介面入手,逐步建構自訂命令、路由命令、命令綁定,並示範命令與資料綁定深度整合 (含 IValueConverter 與 DataTrigger 兩種動態指派命令技巧)。
ICommand 或繼承 RoutedCommandExecute 與 CanExecuteCommand、CommandParameter、CommandTargetpublic interface ICommand {
event EventHandler CanExecuteChanged;
bool CanExecute(object parameter);
void Execute(object parameter);
}
public class Exit : ICommand {
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter) { Application.Current.Shutdown(); }
}
public static class WinCommand {
public static readonly ICommand ExitCommand = new Exit();
}
<MenuItem Header="_File">
<MenuItem Header="_Exit" Command="{x:Static local:WinCommand.ExitCommand}"/>
</MenuItem>
RoutedCommand 透過視覺樹路由 Executed/CanExecute 事件,需在擁有者 (如 Window) 加入 CommandBinding 連接處理邏輯。
<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>
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(); }
}
場景:列出 C:\ 檔案,.txt 檔可開啟 (OpenCommand),其他檔案顯示封鎖 (BlockedCommand)。
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,
(s, e) => Process.Start("notepad.exe", (string)e.Parameter)));
CommandBindings.Add(new CommandBinding(BlockedCommand,
(s, e) => MessageBox.Show((string)e.Parameter, "Blocked")));
FileInfo[] fileList = new DirectoryInfo(@"C:\").GetFiles("*.*");
lbFile.ItemsSource = fileList;
}
<ListBox Name="lbFile" Margin="2">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Path=Name}"/>
<Button Margin="5" CommandParameter="{Binding Path=FullName}" Content="Show">
<Button.Command>
<Binding>
<Binding.Converter>
<local:FileToCommandConverter/>
</Binding.Converter>
</Binding>
</Button.Command>
</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();
return (ext == ".txt") ? WinCommandAndBinding.OpenCommand : WinCommandAndBinding.BlockedCommand;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
<ListBox Name="lbFile2" Margin="2">
<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(...) { throw new NotImplementedException(); }
}
WPF 命令系統提供強大的解耦與可擴充性:
ICommand 適合簡單獨立動作 (如關閉視窗)RoutedCommand + CommandBinding 適合需路由、鍵盤捷徑、多控件共用的應用級命令IValueConverter 或 DataTrigger 實現「資料驅動命令」動態行為本文範例展示從基本用法到進階資料綁定整合的完整鏈路,為 MVVM 架構奠定堅實基礎。