2013-07-24

WPF建立自訂的Command

WPF強大的功能之一,是提供了一些系統預設Command,及可以讓使用者自訂Command的功能。

什麼是Command(命令)呢?簡單來說,我們的程式可能有好多地方要執行同一個功能,拿「複製」來講好了,可能功能表、工具列、右鍵快速功能表、快速鍵都要執行同一個功能,以前傳統視窗寫法就是在物件上點兩下,去編輯事件函數的功能,或許類似物件的事件函數參數相同,但也遇到不同型態的事件函數,這樣就無法共用,變成同樣的動作要寫好多次。

而WPF提供了Command功能,只要在程式中指定好命令名稱和該做的動作之後,任何可以使用Command功能的物件只要在XAML裡面指定Command=”命令名稱”屬性,就可以做到相同的事情了~超簡單啊!!



.net裡面預設有很多已經建立好的Command,譬如ApplicationCommands.Cut就是個「剪下」的Command(注意,這只是個名稱,並沒有實作功能,要實作必須處理Executed的事件),但有時候我們要的功能並沒有提供,像「離開程式」就沒有可對應的Command,這時我們就必須自己製作一個。

目標:按下Ctrl+W快速鍵就能夠關閉程式


方法一:


  1. 在XAML中,<Window.Resources>區段中建立一個RoutedUICommand
    <Window.Resources>
        <RoutedUICommand x:Key="Quit" Text="離開程式" />
    </Window.Resources>

    這樣就建立了一個名為”Quit”的自訂Command定義。
  2. 再來我們要建立快速鍵的指定,在<Window.InputBindings>裡面加入:
    <Window.InputBindings>
        <KeyBinding Gesture="Ctrl+W" Command="{StaticResource Quit}" />
    </Window.InputBindings>

    這樣我們就將Quit的Command與快速鍵Ctrl+W連結在一起了,在任何地方按這個快速鍵就會去執行Quit。
  3. 但現在還沒有任何可執行的功能,所以我們要來指定一下Quit這個Command的實際處理程序。Command總共分CanExecute和Executed這兩個事件,CanExecute回傳是否可執行這個命令,我們可在面做些判斷;而Executed這個事件就是實際去執行該有的動作啦。首先我們先在原始CS檔裡面撰寫好這兩個命令的函數:
    /// <summary>
    /// Quit命令的實際執行動作
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    void Quit_Executed(object sender, ExecutedRoutedEventArgs args) {
        this.Close();
    }
    /// <summary>
    /// 判斷是否能執行Quit命令
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    void Quit_CanExecute(object sender, CanExecuteRoutedEventArgs args) {
        args.CanExecute = true; //這裡永遠回傳true,表示永遠都可執行,可自己加入譬如正在跑什麼動作的話就回傳false
    }
  4. 接著我們就來綁定這兩個事件,在<Window.CommandBindings>裡面新增一個CommandBinding:
    <Window.CommandBindings>
        <CommandBinding 
            Command="{StaticResource Quit}" 
            CanExecute="Quit_CanExecute" 
            Executed="Quit_Executed" />
    </Window.CommandBindings>
  5. 做到此,我們的程式已經有了快速鍵Ctrl+W離開程式的功能啦~若希望其他物件點下去也能有這功能,只要加入屬性Command=”{StaticResource Quit}”就可以囉~

方法二:

如果你覺得寫Class比較帥,而且可以自己定義命名空間方便管理的話,也可以完全透過程式來達成。
  1. 新增一個Class,也順便指定一下Namespace(這裡假設我的專案叫MyProject)
    using System.Windows.Input;
     
    namespace MyProject.MyCommands
    {
        class SystemCommands
        {
            /// <summary>
            /// 關閉應用程式的自訂Command
            /// </summary>
            public static RoutedUICommand Quit = new RoutedUICommand("離開應用程式", "QuitCommand", typeof(MainWindow));
     
        }
    }
  2. 一樣如方法一的步驟三,先新增兩個事件的處理函數。
  3. 在主XAML的程式碼檔的建構子裡面,增加CommandBinding物件:
    CommandBinding cbQuit = new CommandBinding(
        MyProject.MyCommands.SystemCommands.Quit,
        new ExecutedRoutedEventHandler(Quit_Executed), 
        new CanExecuteRoutedEventHandler(Quit_CanExecute)
    );
    //加到CommandBindings裡面
    this.CommandBindings.Add(cbQuit);
  4. 再增加InputBinding物件:
    InputBinding ibQuit = new InputBinding(
        MyProject.MyCommands.SystemCommands.Quit,
        new KeyGesture(Key.W, ModifierKeys.Control) //就是Ctrl+W
    );
    //加到InputBindings裡面
    this.InputBindings.Add(ibQuit);
  5. 整個段完成如下:
    public MainWindow() {
        InitializeComponent();
     
        CommandBinding cbQuit = new CommandBinding(
            MyProject.MyCommands.SystemCommands.Quit,
            new ExecutedRoutedEventHandler(Quit_Executed), 
            new CanExecuteRoutedEventHandler(Quit_CanExecute)
        );
        this.CommandBindings.Add(cbQuit);
     
        InputBinding ibQuit = new InputBinding(
            MyProject.MyCommands.SystemCommands.Quit,
            new KeyGesture(Key.W, ModifierKeys.Control)
        );
        this.InputBindings.Add(ibQuit);
    }
  6. 回到XAML裡,在頂部元素<Window>裡面原有的xmlns底下加上自訂的命名空間:
    xmlns:My="clr-namespace:MyProject.MyCommands"
  7. 這樣有物件要使用該命令的話,只需增加底下屬性即可:
    Command="My:SystemCommands.Quit"
這樣就大功告成啦~當然你要把兩個方法結合也是可以的,我的作法就是寫自訂Command的Class,在XAML裡面指定CommandBinds和InputBinds,然後加入自訂的命名空間,這樣設計和程式撰寫對我來說是比較直觀的。

參考資料:
http://msdn.microsoft.com/zh-tw/magazine/cc785480.aspx

http://www.cnblogs.com/gnielee/archive/2010/07/16/wpf-custom-hotkey-command.html