2008-08-12 22 views
34

Ich habe einige WPF-Programmierung gemacht und eine Sache, die ich nie bekam, war das Befehlsmuster. Jedes Beispiel scheint für eingebaute, edit, cut, paste zu sein. Jeder hat ein Beispiel oder einen Vorschlag für Best Practice für benutzerdefinierte Befehle?Benutzerdefinierte WPF-Befehlsmuster Beispiel

Antwort

38

Ah ha! Eine Frage, die ich beantworten kann! Zunächst einmal sollte ich erwähnen, dass es für mich persönlich einfacher war, Befehle in Code anstatt in XAML zu definieren und zu verbinden. Es erlaubt mir, die Handler für die Befehle ein wenig flexibler anzubinden, als dies bei einem XAML-Ansatz der Fall ist.

Sie sollten herausfinden, welche Befehle Sie haben möchten und was sie betreffen. In meiner Anwendung habe ich zur Zeit eine Klasse für die Definition wichtige Anwendungsbefehle wie folgt:

public static class CommandBank 
{ 
    /// Command definition for Closing a window 
    public static RoutedUICommand CloseWindow { get; private set; } 

    /// Static private constructor, sets up all application wide commands. 
    static CommandBank() 
    { 
    CloseWindow = new RoutedUICommand(); 
    CloseWindow.InputGestures.Add(new KeyGesture(Key.F4, ModifierKeys.Alt)); 
    // ... 
    } 

Nun, da ich den Code alle zusammen mit einem Code nur Annäherung an Befehle halten wollte mich lets setzen die folgenden Methoden in die Klasse oben:

/// Closes the window provided as a parameter 
public static void CloseWindowExecute(object sender, ExecutedRoutedEventArgs e) 
{ 
    ((Window)e.Parameter).Close(); 
} 

/// Allows a Command to execute if the CommandParameter is not a null value 
public static void CanExecuteIfParameterIsNotNull(object sender, CanExecuteRoutedEventArgs e) 
{ 
    e.CanExecute = e.Parameter != null; 
    e.Handled = true; 
} 

die zweite dort Methode kann auch mit anderen Befehlen geteilt werden, ohne dass ich es über den ganzen Ort wiederholen zu müssen.

Sobald Sie die Befehle wie folgt definiert haben, können Sie sie zu jedem UI-Element hinzufügen. Nachdem das Fenster geladen wurde, füge ich Befehlsbindungen sowohl dem Window als auch dem MenuItem hinzu und füge dann dem Fenster eine Eingabebindung hinzu, die eine Schleife für alle Befehlsbindungen verwendet. Der Parameter, der übergeben wird, ist das Fenster selbst, also weiß der obige Code, welches Fenster er versuchen soll zu schließen.

public partial class SimpleWindow : Window 
{ 
    private void WindowLoaded(object sender, RoutedEventArgs e) 
    { 
    // ... 
    this.CommandBindings.Add(
     new CommandBinding(
     CommandBank.CloseWindow, 
     CommandBank.CloseWindowExecute, 
     CommandBank.CanExecuteIfParameterIsNotNull)); 

    foreach (CommandBinding binding in this.CommandBindings) 
    { 
     RoutedCommand command = (RoutedCommand)binding.Command; 
     if (command.InputGestures.Count > 0) 
     { 
     foreach (InputGesture gesture in command.InputGestures) 
     { 
      var iBind = new InputBinding(command, gesture); 
      iBind.CommandParameter = this; 
      this.InputBindings.Add(iBind); 
     } 
     } 
    } 

    // menuItemExit is defined in XAML 
    menuItemExit.Command = CommandBank.CloseWindow; 
    menuItemExit.CommandParameter = this; 
    // ... 
    } 

    // .... 
} 

ich dann später auch Event-Handler für die Window und WindowClosed Ereignisse haben, ich empfehlen Ihnen, die tatsächliche Umsetzung von Befehlen so klein und allgemein wie möglich zu machen. Wie in diesem Fall habe ich nicht versucht, Code zu schreiben, der versucht, das Schließen des Fensters zu stoppen, wenn nicht gespeicherte Daten vorhanden sind. Ich habe diesen Code fest im WindowClosing-Ereignis gespeichert.

Lassen Sie mich wissen, wenn Sie weitere Fragen haben. :)

+2

Ihre CanExecuteIfParameterIsNotNull enthält eine meiner Haustier Belästigungen. Warum nicht einfach e.CanExecute = e.Parameter! = Null; – Ray

+0

@Ray Das war nicht etwas, von dem ich wusste, dass ich es tun konnte! Danke für die Bearbeitung Drew. :) – Nidonocu

3

Die Sache mit XAML ist, dass es für 'einfache' Programme in Ordnung ist, aber leider funktioniert es nicht gut, wenn Sie Dinge wie Teilen Funktionen tun wollen. Angenommen, Sie haben mehrere Klassen und UIs mit Befehlen, die nie deaktiviert wurden, müssten Sie für jedes Window oder UserControl eine 'CanAlwaysExecute'-Methode schreiben! Das ist einfach nicht sehr DRY.

Nachdem ich mehrere Blogs gelesen habe und verschiedene Dinge ausprobiert habe, habe ich die Wahl getroffen, XAML rein auf Looks, Styles, Animationen und Trigger zu setzen. All meine Verbindung von Event-Handlern und Kommandos ist jetzt im Code-Behind. :)

Ein weiteres Problem ist übrigens Eingabe-Bindung, damit sie gefangen werden können, Fokus muss auf das Objekt, das die Eingabe-Bindungen enthält. Um beispielsweise eine Abkürzung zu verwenden, die Sie jederzeit verwenden können (z. B. F1 zum Öffnen der Hilfe), muss diese Eingabebindung für das Window-Objekt festgelegt werden, da diese immer den Fokus hat, wenn Ihre App Active ist. Die Verwendung der Codemethode sollte dies erleichtern, auch wenn Sie UserControls verwenden, die Eingabebindungen zu ihrem übergeordneten Fenster hinzufügen möchten.

7
+2

http://blogs.vertigo.com/personal/alanl/Blog/archive/2007/05/31/commands-in-wpf.aspx dieser Link ist tot :( – Drahakar