首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 命令系統深度解析:從 ICommand 到命令與資料綁定整合

     編輯:關於.NET

前言

WPF 命令系統將「語義動作」與「觸發介面」解耦,支援多控件共用同一命令、自動啟用/禁用 UI、鍵盤捷徑關聯、MVVM 綁定。本文從 ICommand 介面入手,逐步建構自訂命令、路由命令、命令綁定,並示範命令與資料綁定深度整合 (含 IValueConverter 與 DataTrigger 兩種動態指派命令技巧)。

一、命令三要素

  1. 定義命令:實作 ICommand 或繼承 RoutedCommand
  2. 實作命令邏輯:Execute 與 CanExecute
  3. 建立觸發器:設定控件 Command、CommandParameter、CommandTarget

二、ICommand 介面與自訂命令

public 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();
}

XAML 綁定靜態命令

<MenuItem Header="_File">
    <MenuItem Header="_Exit" Command="{x:Static local:WinCommand.ExitCommand}"/>
</MenuItem>

三、RoutedCommand 與 CommandBinding (WPF 內建路由機制)

RoutedCommand 透過視覺樹路由 Executed/CanExecute 事件,需在擁有者 (如 Window) 加入 CommandBinding 連接處理邏輯。

範例:視窗關閉命令 (ApplicationCommands.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>

後台加入 CommandBinding

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)。

1. 定義兩個 RoutedCommand

public static readonly RoutedCommand OpenCommand = new RoutedCommand("Open", typeof(WinCommandAndBinding));
public static readonly RoutedCommand BlockedCommand = new RoutedCommand("Blocked", typeof(WinCommandAndBinding));

2. 註冊 CommandBinding (含 Lambda 處理)

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;
}

3. 方案 A:IValueConverter 依副檔名轉換命令

<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();
    }
}

4. 方案 B:DataTrigger 切換按鈕 Command 與 Content

<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 架構奠定堅實基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved