Frage

Betrachten

die Referenz Josh Smith‘Artikel WPF Apps Mit der Model-View-Viewmodel Design-Muster , insbesondere die beispielhafte Implementierung eines RelayCommand (in Figur 3). (Keine Notwendigkeit, durch den gesamten Artikel für diese Frage zu lesen.)

Generell denke ich, die Umsetzung ist ausgezeichnet, aber ich habe eine Frage über die Delegation von CanExecuteChanged Abonnements für die CommandManager Veranstaltung RequerySuggested. Die Dokumentation für RequerySuggested lautet:

  

Da dieses Ereignis statisch ist, wird es   nur halten, auf die Behandlungsroutine als schwaches   Referenz. Objekte, die hören für   Dieses Ereignis sollte eine starke halten   Referenz auf ihre Event-Handler   vermeidet es Müll gesammelt werden. Diese   kann, indem ein durchgeführt werden   Privat Feld und Zuordnen der   Handler als Wert vor oder nach   Anbringen auf dieses Ereignis.

Doch die Beispielimplementierung von RelayCommand hält keine solche an den abonnierten Handler:

public event EventHandler CanExecuteChanged
{
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
}
  1. Ist dies das schwache Referenzleck bis zu dem RelayCommand des Kunden zu verlangen, dass der Benutzer des RelayCommand der Umsetzung der CanExecuteChanged verstehen und pflegen einer Live-Referenz selbst?
  2. Wenn ja, ist es sinnvoll zu sein, zum Beispiel, ändern Sie die Implementierung von RelayCommand so etwas wie die folgenden zu mildern das Potenzial vorzeitige GC des CanExecuteChanged Teilnehmers zu sein:

    // This event never actually fires.  It's purely lifetime mgm't.
    private event EventHandler canExecChangedRef;
    public event EventHandler CanExecuteChanged
    {
        add 
        { 
            CommandManager.RequerySuggested += value;
            this.canExecChangedRef += value;
        }
        remove 
        {
            this.canExecChangedRef -= value;
            CommandManager.RequerySuggested -= value; 
        }
    }
    
War es hilfreich?

Lösung

ich glauben, dass diese Implementierung fehlerhaft ist , weil es auf jeden Fall den schwachen Verweis auf den Event-Handler-Lecks. Das ist etwas wirklich sehr schlecht.
Ich bin mit dem MVVM Light Toolkit und den RelayCommand implementiert darin, und es wird ebenso wie in dem Artikel implementiert.
Der folgende Code wird nie invoke OnCanExecuteEditChanged:

private static void OnCommandEditChanged(DependencyObject d, 
                                         DependencyPropertyChangedEventArgs e)
{
    var @this = d as MyViewBase;
    if (@this == null)
    {
        return;
    }

    var oldCommand = e.OldValue as ICommand;
    if (oldCommand != null)
    {
        oldCommand.CanExecuteChanged -= @this.OnCanExecuteEditChanged;
    }
    var newCommand = e.NewValue as ICommand;
    if (newCommand != null)
    {
        newCommand.CanExecuteChanged += @this.OnCanExecuteEditChanged;
    }
}

Allerdings, wenn ich es so ändern, wird es funktionieren:

private static EventHandler _eventHandler;

private static void OnCommandEditChanged(DependencyObject d,
                                         DependencyPropertyChangedEventArgs e)
{
    var @this = d as MyViewBase;
    if (@this == null)
    {
        return;
    }
    if (_eventHandler == null)
        _eventHandler = new EventHandler(@this.OnCanExecuteEditChanged);

    var oldCommand = e.OldValue as ICommand;
    if (oldCommand != null)
    {
        oldCommand.CanExecuteChanged -= _eventHandler;
    }
    var newCommand = e.NewValue as ICommand;
    if (newCommand != null)
    {
        newCommand.CanExecuteChanged += _eventHandler;
    }
}

Der einzige Unterschied? Genau wie in der Dokumentation von CommandManager.RequerySuggested angegeben ich die Event-Handler in einem Feld speicherte.

Andere Tipps

Ich habe die Antwort in Joshs Kommentar auf seine " Legendes Routed Commands " Artikel:

  

[...] müssen Sie das WeakEvent Muster in Ihrem CanExecuteChanged verwenden   Veranstaltung. Dies liegt daran, visuelle Elemente wird dieses Ereignis Haken und seit   das Befehlsobjekt vielleicht nie Müll, bis der App gesammelt werden   schalte mich ab, dort für ein Speicherleck ein sehr reales Potential ist. [...]

Das Argument scheint, dass CanExecuteChanged Implementierer zu sein nur schwach an den registrierten Handler halten müssen, da WPF Visuals zu dumm sind, sich zu aushängen. Dies geschieht am einfachsten durch die Delegation an die CommandManager umgesetzt, die dies bereits der Fall ist. Vermutlich aus dem gleichen Grund.

Nun, nach Reflector ist es die gleiche Art und Weise in der RoutedCommand Klasse implementiert, so dass ich denke, es in Ordnung sein muss ... es sei denn, jemand in dem WPF-Team einen Fehler gemacht;)

Ich glaube, es fehlerhaft ist.

Durch die Ereignisse an den Befehlsmanager Umleiten, Sie erhalten das folgende Verhalten

  

Damit wird sichergestellt, dass der WPF befehl   Infrastruktur fragt allen RelayCommand   Objekte, wenn sie ausgeführt werden kann, wann immer   es fragt die integrierten Befehle.

Doch was passiert, wenn Sie alle Regler auf einen einzigen Befehl neu zu bewerten Status CanExecute gebunden informieren wollen? In seiner Umsetzung, müssen Sie auf die Befehlsmanager gehen, was bedeutet,

Jeder einzelne Befehl in der Anwendung verbindlich wird neu bewertet

, dass alle diejenigen umfasst, die einen Hügel von Bohnen spielen keine Rolle, wo die, die CanExecute Auswertung hat Nebenwirkungen (wie Datenbankzugriff oder lang laufende Tasks), diejenigen, die werden darauf warten, gesammelt ... Es ist wie mit einem Vorschlaghammer einen friggen Nagel einzuschlagen.

Sie müssen ernsthaft die Auswirkungen, dies zu tun betrachten.

Ich kann den Punkt hier fehlt aber nicht die nach dem starken Bezug auf die Event-Handler in der contructor dar?

    _canExecute = canExecute;           
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top