2010-02-19 9 views
7

Ich bin immer noch grokking angehangenen Verhaltensweisen im Allgemeinen, und bin nicht in der Lage zu sehen, wie man einen Unit-Test für einen schreiben.Unit-Test ein angehängtes Verhalten wpf

Ich habe etwas Code unten aus Sacha Barbers Cinch-Framework eingefügt, der es erlaubt, ein Fenster über angehängtes Verhalten zu schließen. Kann mir jemand einen Beispiel-Unit-Test zeigen?

Danke!
Berryl

#region Close 

    /// <summary>Dependency property which holds the ICommand for the Close event</summary> 
    public static readonly DependencyProperty CloseProperty = 
     DependencyProperty.RegisterAttached("Close", 
      typeof(ICommand), typeof(Lifetime), 
       new UIPropertyMetadata(null, OnCloseEventInfoChanged)); 

    /// <summary>Attached Property getter to retrieve the CloseProperty ICommand</summary> 
    public static ICommand GetClose(DependencyObject source) 
    { 
     return (ICommand)source.GetValue(CloseProperty); 
    } 

    /// <summary>Attached Property setter to change the CloseProperty ICommand</summary> 
    public static void SetClose(DependencyObject source, ICommand command) 
    { 
     source.SetValue(CloseProperty, command); 
    } 

    /// <summary>This is the property changed handler for the Close property.</summary> 
    private static void OnCloseEventInfoChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     var win = sender as Window; 
     if (win == null) return; 

     win.Closing -= OnWindowClosing; 
     win.Closed -= OnWindowClosed; 

     if (e.NewValue == null) return; 

     win.Closing += OnWindowClosing; 
     win.Closed += OnWindowClosed; 
    } 

    /// <summary> 
    /// This method is invoked when the Window.Closing event is raised. 
    /// It checks with the ICommand.CanExecute handler 
    /// and cancels the event if the handler returns false. 
    /// </summary> 
    private static void OnWindowClosing(object sender, CancelEventArgs e) 
    { 
     var dpo = (DependencyObject)sender; 
     var ic = GetClose(dpo); 
     if (ic == null) return; 

     e.Cancel = !ic.CanExecute(GetCommandParameter(dpo)); 
    } 

    /// <summary> 
    /// This method is invoked when the Window.Closed event is raised. 
    /// It executes the ICommand.Execute handler. 
    /// </summary> 
    static void OnWindowClosed(object sender, EventArgs e) 
    { 
     var dpo = (DependencyObject)sender; 
     var ic = GetClose(dpo); 
     if (ic == null) return; 

     ic.Execute(GetCommandParameter(dpo)); 
    } 

    #endregion 

Antwort

5

Sie würden wahrscheinlich eine Lambda in Ihrem ICommand mit einem DelegateCommand oder RelayCommand verwenden. Mehrere Implementierungen von diesen existieren überall und Cinch kann etwas ähnliches haben. Wirklich einfache Version (als Beispiel, nicht für den produktiven Einsatz gedacht):

public class DelegateCommand : ICommand { 
    private Action _execute = null; 

    public void Execute(object parameter) { 
     _execute(); 
    } 

    public DelegateCommand(Action execute) { 
     _execute = execute; 
    } 

    #region stuff that doesn't affect functionality 
    public bool CanExecute(object parameter) { 
     return true; 
    } 
    public event EventHandler CanExecuteChanged { 
     add { } 
     remove { } 
    } 
    #endregion 
} 

Dann wird Ihr Testkörper wie folgt aussehen könnte:

bool wascalled = false; 

var execute = new DelegateCommand(
    () => { 
     wascalled = true; 
    }); 

var window = new Window(); 
SomeClass.SetClose(window, execute); 

// does the window need to be shown for Close() to work? Nope. 

window.Close(); 

AssertIsTrue(wascalled); 

Dies ist eine stark vereinfachte Beispiel. Es gibt natürlich andere Tests, die Sie durchführen möchten. In diesem Fall sollten Sie eine vollständigere Implementierung von DelegateCommand erstellen oder finden, die unter anderem auch CanExecute ordnungsgemäß implementiert.

+0

Cinch geht einen Schritt weiter mit dem RelayCommand, indem Sie Ihren wiscalled Test in eine CommandSucceeded bool Eigenschaft backen. Ihr Beitrag ist hilfreich bei der Durchsetzung, dass SetClose am Ende des Tages immer noch ein Eigenschaften-Setter ist, auch wenn es nicht wie die einfacheren normalen C# -Eigenschaften-Setter aussieht! Das ist eines der Dinge, die ich nicht sah und die für mich noch nicht intuitiv ist. Prost – Berryl

+0

Ja. Beim Kompilieren werden diese statischen Get/Set-Methoden aufgerufen. Dasselbe gilt für DPs: Es überspringt den Property Wrapper und ruft 'SetValue' /' GetValue' direkt auf dem 'DependencyObject' auf. Gut zu hören in Cinch. Ich habe es noch nicht gelesen. –

3

DependencyProperty verändern und Wert Zwang auf ihrem eigenen Aussehen wie 'Impossible Abhängigkeiten' für mich. Ein Verweis auf Windows macht die Sache noch schwieriger. Ich denke, dass ich hier mit Humble Object pattern gehen würde ...

+0

Ich kann in diesem Fall tatsächlich mit dem Fenster leben, aber das ist ein interessanter Link. Können Sie in diesem Fall veranschaulichen, wie Sie das Humble Object Pattern anwenden würden? Prost – Berryl

Verwandte Themen