Blue Orange Green Pink Purple

Posts Tagged ‘PostSharp’

You can use the search form below to go through the content and find a specific post or page:

Feb 27

INotifyPropertyChanged mit Hilfe von AOP lösen

Heute möchte ich einmal eine Technik vorstellen, die ich schon öfters bei meinen WPF Projekten eingesetzt habte. Verwendet man in WPF die MVVM-Architektur (ModelView – View – Model), so kommt man um das Konzept des mächtigen WPF Databindings nicht herum. Wer mit Databinding noch nicht so vertraut ist, soll sich zuerst mal hier http://msdn.microsoft.com/en-us/library/ms750612.aspx etwas schlau machen.

Das interessante am WPF Databinding ist die Möglichkeit, Werte bidirektional zu binden: Das heißt, dass man einerseits die Daten von der Datenquelle zur Datensenke binden kann und andererseits auch Änderungen der Datensenke automatisch in die Datenquelle übernehmen kann. In MVVM würde das bedeuten, dass man auf der View (XAML) UI-Controls aus Daten des ViewModels befüllen kann und Änderungen, die auf der View auftreten automatisch zurück in das ViewModel speichern kann, ohne dabei eine Benutzerinteraktion zu benötigen. Wichtig ist auch, dass bereits gebundene Werte auf der View automatisch aktualisiert werden sollen, sobald im ViewModel – also bei der Datenquelle – Änderungen auftreten.

INotifyPropertyChanged oder DependencyProperties
Um ein automatisches Binding-Update zu erreichen, gibt es zwei unterschiedliche Wege, das zu erreichen:

  1. Man leitet eine Klasse von DependencyObject ab und implementiert DependencyProperties. Da ein ViewModel normalerweise nicht von DependencyObject ableitet, ist diese Möglichkeit im Kontext von MVVM nicht wirklich brauchbar. Mehr über DependencyObject und DependencyProperty gibts unter http://msdn.microsoft.com/en-us/library/system.windows.dependencyobject.aspx
  2. Man implementiert das Interface INotifyPropertyChanged (http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx). Dieses Interface implementiert einen Event names PropertyChanged, der gefeuert wird, sobald sich eine Property bzw. der Wert einer Property verändert hat. Bindet man nun in der View (XAML) auf eine Property eines ViewModels, muss diese Property jedesmal, wenn sich der Wert der Property verändert, diesen Event feuern, um automatisch das Databinding zu aktualisieren. Wirft es den Event nicht, gibts auch keine automatische Aktualisierung der View.

Das war mal das Grundgerüst, dass wir für unsere weiteres Thema heute benötigen.

Wo ist das Problem?
Sehen wir uns einmal folgende Klasse an, die INotifyPropertyChanged implementiert und ein paar Properties für das Databinding zur Verfügung stellt:


public class ApplicationViewModel : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;

    private string _ApplicationTitleField = string.Empty;
    public string ApplicationTitle
    {
       get { return _ApplicationTitleField; }
       set
       {
          _ApplicationTitleField = value;
          if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("ApplicationTitle"));
       }
    }

    private string _ApplicationVersionField = string.Empty;
    public string ApplicationVersion
    {
        get { return _ApplicationVersionField; }
        set
        {
           _ApplicationVersionField = value;
           if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("ApplicationVersion"));
        }
     }
}

So, wo ist hier jetzt das Problem? Genau, die Properties selbst. Im Grunde genommen ist es kein Problem bzw. ist syntaktisch alles in Ordnung. Ich persönlich habe aber ein Problem damit, aus den Automatic Properties “alte .NET 2.0″ Properties zu erzeugen, nur um im Setter der Property den PropertyChanged Event feuern zu können. Wie lösen wir das Ganze jetzt: Wir verwenden ein tolle Bibliothek names PostSharp, die die Prinzipen von AOP (Aspect Oriented Programming) implementiert.

Lösung: AOP mittel PostSharp
Mit AOP können wir Funktionalität in sogenannte Aspekte auslagern und diese nach Bedarf wiederverwenden. Genau so einen Aspekt können wir jetzt für unser Problem mit dem PropertyChanged Event einsetzen. Wir können hergehen und uns selbst ein die notwendige Funktionalität programmieren oder wir verwenden bereits vorhandene Bibliotheken, die uns bei unserer Problemlösung unterstützen.

In der Community gibt es mittlerweile eine handvoll verfügbaren Bibliotheken, die AOP Funktionalität für .NET zur Verfügung stellen. Mein persönlicher Favorit ist auf alle Fälle PostSharp entwickelt von Gael Fraiteur. Einer der großen Vorteile von PostSharp ist , dass sich die Bibliothek in den MSBuild Process hängt, direkt den CIL Code manipuliert und somit keine Perfomanceeinbussen entstehen. Wie gehen wir jetzt also vor, um unser Problem der alten .NET Properties mit Hilfe eines Aspekts zu lösen?

  1. Zuerst laden wir uns mal PostSharp herunten und installieren es: http://www.sharpcrafters.com/postsharp/download
  2. Um PostHsarp verwenden zu können, müssen wir 2 Referenzen zu unserem Projekt hinzufügen: PostSharp.Public und PostSharp.Laos
  3. Dann erstellen wir uns den neuen Aspekt. Im Grunde ist das eine einfach Klasse, die von einer bestimmten Basisklasse ableitet und als Serializeable gekenntzeichnet werden muss:
    [Serializable]
    public class NotifyAspect : OnMethodBoundaryAspect
    {
    }
    
  4. Die Basisklasse OnMethodBoundaryAspect stellt PostSharp zur Verfügung. Diese Klasse bietet eine Methode names OnExit an, die wir in unserem Apsekt einfach überschreiben und implementieren. Diese Methode wird aufgerufen, wenn eine Methode verlassen wird — also wenn das Ende einer Methode erreicht wurde. Da wir unseren Aspekt für Properties verwenden, ist unsere Methode eigentlich der Getter bzw. Setter – Zugriff der Property:
    public override void OnExit(MethodExecutionEventArgs eventArgs)
    {
       if (!(eventArgs.Instance is ViewModelBase))
          throw new InvalidCastException("Cannot raise PropertyChanged on viewmodel that doesn't derived from ViewModelBase");
    
       if (!eventArgs.Method.IsSetter()) return;
    
       ViewModelBase viewModel = eventArgs.Instance as ViewModelBase;
    
       viewModel.OnPropertyChanged(eventArgs.Method.SetterName());
    }
    

    Wir prüfen,ob die Instanz, zu der die Methode gehört von einer bestimmten Basisklasse ist. Ist das nicht so, dann werfen wir einen Fehler, da der Aspekt nur mit bestimmen Typen umgehen kann. Warum? Weil wir später eine Methode der Instanz aufrufen müssen. Jetzt überprüfen wir, ob die Methode, für die der Aspekt aufgerufen wird eigentlich eine Setter-Methode einer Property ist. Das haben wir in eine Extension-Methode ausgelagert (der detaillierte Code ist im Beispielprojekte nachzulesen). Jetzt können wir casten und eine bestimmte Methode der Basisklasse aufrufen: OnPropertyChanged wirft im Endeffekt den PropertyChanged Event des Interfaces INotifyPropertyChanged. Diesen Event können wir nur in der Klasse werfen, in der der Event auch definiert wurde. Daher müssen wir hier in unserem Aspekt diesen Umweg gehen.

    public void OnPropertyChanged(string propertyName)
    {
       if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("Propertyname is null or too short.");
    
       PropertyInfo info = this.GetType().GetProperty(propertyName);
       if (info == null) throw new InvalidProgramException("The requested property doesn't exist.");
    
       if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    
  5. Jetzt fehlt uns nur noch, dass wir den Aspekt in unserem ViewModel verwenden:
    public class ApplicationViewModel : ViewModelBase
    {
       [Notify]
       public string ApplicationTitle { get; set; }
    
       [Notify]
       public string ApplicationVersion { get; set; }
    }
    

Wir können uns jetzt eine View bzw. XAML Seite bauen, die dieses ViewModel als DataContext gesetzt hat und auf die beiden Properties databinden. Das Demoprojekt ist wie immer unter http://downloads.juergenoberngruber.at/blog/INotifyPropertyChangedWithAOP.zip zu finden.

In diesem Sinne.

  • Recent Posts
    • HowTo: Eigene Regeln für Microsoft StyleCop erstellen
    • HowTo: Microsoft StyleCop Integration mit Visual Studio und MSBuild
    • HowTo: Eigene Templates für Visual Studio 2008 erstellen, Teil 2
    • HowTo: Eigene Templates für Visual Studio 2008 erstellen, Teil 1
    • Performance des Cassini Webservers in Kombination mit Firefox
  • Archives
    • June 2010 (2)
    • May 2010 (3)
    • March 2010 (3)
    • February 2010 (4)
    • January 2010 (3)
    • December 2009 (1)
    • November 2009 (9)
  • Tags
    .net AOP ASP.NET ASP.NET MVC blend Bootmanager C# ci Codequality Configuration Continuous Integration css Cursor DateTime DDD Deployment dynamisch emit Exrpession Extensibility Fluent HowTo Microsoft MVVM Pattern PostSharp Reflection Repository ruby silverlight Software Design StyleCop System teamcity Templates VHD Virtual Images Visual Studio Vorlagen web Windows 7 Windows Mobile WinForms WPF XAML
  • About

    Jürgen Oberngruber is a project manager and software architect living in Wels, Austria and currently working at ecomplexx Austria, Wels. During his study at the University of Applied Sciences in Hagenberg, Austria he gained a deep knowledge in the field of software engineering using a lot of different programming languages. Since a few years he's focusing on the Microsofts .NET platform including all relevant technologies. One of his passion is to explore, to test and to evaluate new technologies and programming languages (mostly in the field of Microsofts .NET platform). Checkout more information on www.juergenoberngruber.at

  • Meta
    • Log in
    • Entries RSS
    • Comments RSS
    • WordPress.org
  • Archives
    • June 2010
    • May 2010
    • March 2010
    • February 2010
    • January 2010
    • December 2009
    • November 2009
  • Search






  • Home

© Copyright Jürgen Oberngruber's Blog. All rights reserved.
Designed by FTL WordPress Themes brought to you by DT Web Template

Back to Top