Home > Softwareentwicklung > INotifyPropertyChanged – Varianten für die Implementierung

INotifyPropertyChanged – Varianten für die Implementierung

September 2nd, 2009 Bernd

Wer mit WPF programmiert, kennt INotifyPropertyChanged. Das allgegenwärtige Interface, mit dem Änderungen z.B. von einem ViewModel zu einem daran gebunden UI Element propagiert werden. Eine typische Implementierung von INotifyPropertyChanged sieht etwa so aus:

INotifyPropertyChanged Horror

Drei Sachen fallen ins Auge

  1. jedes ViewModel muss INotifyPropertyChanged implementieren, d.h. den PropertyChanged Event anbieten und eine Methode zum Feuern des Events haben.
  2. die EventArgs für den PropertyChanged Event bekommen als Parameter einen String, der dem Namen der Property entspricht.
  3. der Code ist “zugemüllt” mit Infrastrukturcode, der eigentlich nichts zur Logik des Objekts beiträgt
    Der erste Punkt lässt sich relativ einfach lösen, indem man eine ViewModel Basisklasse macht, die diese Aufgaben übernimmt.
    Basisklasse für ViewModels
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Beim zweiten Punkt wird es dann schon schwieriger. Hier gibt es mehrere Ansätze, die Strings loszuwerden.

Am häufigsten trifft man wohl eine Variante mit Lambda Expressions an. Die Implementierungen reichen von einer Extensionmethod für den PropertyChangedEventHandler bis zu einer generischen ViewModel Basisklasse, welche das abgeleitete ViewModel als Type Parameter bekommt. Hier ein paar Links zum Nachlesen:

J.P. Hamilton – Generische ViewModel Basisklasse

Stefan Lieser – Implementierung direkt im ViewModel

Eric De Carufel – Extensionmethod für PropertyChangedEventHandler

 

Andere Lösungen, die ich gefunden habe:

Alkampfer – INotifyPropertyChanged mit CodeDom implementieren

Ayende – INotifyPropertyChanged mit Castle Dynamic Proxy

Mike Saunders – INotifyPropertyChanged mit PostSharp Aspekt

Richard Banks – INotifyPropertyChanged mit PostSharp Aspekt

 

Dann gibt es noch die den Verfechter von UpdateControls, Michael L. Perry. UpdateControls ist eine Open Source Bibliothek auf CodePlex, die es erlaubt, Änderungen von Eigenschaften auch ohne INotifyProperyChanged zu propagieren und z.B. an WPF Controls zu binden. Eine gute Einführung in UpdateControls kann man beim CODE Magazin lesen.

 

Mir gefallen bisher die Lösungen mit PostSharp am Besten. Damit lassen sich alle drei Punkte auf einmal erschlagen.

  1. Mit einem PostSharp Attribut, das auf die ViewModel Klasse geklebt wird, lässt man die INotifyPropertyChanged Implementierung automatisch einweben.
  2. Es gibt keine Magic Strings mehr, da auch das Feuern des ProperyChanged Events automatisch eingewoben wird. Verschreiber sind dadurch ausgeschlossen, Refactoring ist kein Problem.
  3. Der Code bleibt sauber und lesbar, da der ganze Notifikationscode erst nachträglich hinzugefügt wird.
    Der nächste Schritt

Ein Kollege von mir hatte die Idee, die Verwendung von PostSharp in Verbindung mit dem MVVM Pattern noch einen Schritt weiter zu treiben. Wir verwenden jetzt ein [ViewModelAspekt] PostSharp Attribut, mit dem die ViewModels versehen werden.

[ViewModelAspect]
public class MyViewModel
{
    private string _name;

    public string Name
    {
        get{ return _name;}
        set{ _name = value;}
    }

    public void Save()
    {
        // do sth.
    }
}

Dieser Aspekt macht folgendes:

  • Implementierung von INotifyPropertyChanged
  • Für alle public Properties wird im Setter der PropertyChanged Event gefeuert
  • Für alle public void Methoden wird eine Command Property erzeugt, welche mit Hilfe des Delegate/Relay Commands wiederum diese Methode aufruft.
    Dadurch haben wir es geschafft, den Overhead für die Verwendung des MVVM Patterns sehr gering zu halten. Die nächsten Wochen werden zeigen, ob sich diese Variante bewährt und alle Use Cases damit abgedeckt werden können.
Kick it on dotnet-kicks.de
  1. September 7th, 2009 at 21:59 | #1

    Eine Implementierung wie man sie im PostSharp Aspekt findet, kannte ich noch gar nicht. Bisher habe ich das nur über Extensions gemacht.

    Wie sieht das Performance mäßig aus?

    Getestet habe ich das bei mir so:

    System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
    s.Start();
    NotifyPropertyChanged(this.GetMemberName(x=>x.PropertieModifiers));
    s.Stop();

    Mit dieser Extension brauche ich eine Millesekunde.

    Mit der Übergabe eines strings sind es gerade mal ca. 324 Ticks.

    Ich wüsste jetzt aber auch gar nicht wie ich einen gleichwertigen Test mit dem “PostSharp Aspekt” anstellen kann.

  2. September 8th, 2009 at 09:57 | #2

    Hallo Martin,
    über die Performance mache ich mir erstmal keine Sorgen. Die UI Änderungen kommen nicht so häufig. Wenn man es trotzdem messen will, kann man die Stopwatch in dem Aspekt mit implementieren, anlalog zu dem Beispiel wie Du es gemacht hast. Werde das bei Gelegenheit mal machen und berichten.

    Bernd

  3. Christian
    September 29th, 2009 at 12:01 | #3

    Hallo!

    Wäre es vielleicht möglich das der Quellcode des ViewModel-PostSharp-Aspekts veröffentlicht wird?

    Ich fange grade damit an PostSharp zu lernen und würd es mal interessant finden eine recht komplexe Anwendung davon zu sehn.

    Danke im Vorraus!

  4. Bernd
    September 29th, 2009 at 13:15 | #4

    Hallo Christian,
    leider kann ich den Code für den Aspekt momentan hier nicht posten. Vielleicht in einem zukünftigen Post.

    Bernd

  5. Christian
    September 30th, 2009 at 12:54 | #5

    Ich versuche jetzt gerade eine ähnliche Lösung wie die deinige zu erzeugen und stosse dabei geradezu gegen eine Wand wenn ich versuche den dritten Punkt (Command-Properties für jede public void Methode) zu implementieren.

    Mit welchem Aspect kann man denn Properties zu einer Klasse hinzufügen?

    Danke im Vorraus!

  6. Bernd
    October 6th, 2009 at 15:39 | #6

    Dafür gibt es keinen vorgefertigten Aspekt. Für solch eine Funktionalität musst Du Postsharp Core verwenden und deinen eigenen Weaver schreiben.

    Bernd

  1. No trackbacks yet.
Comments are closed.