Sichere Strings in INotifyPropertyChanged  
Frank Dzaebel, erstellt am: 08.07.2010, zuletzt geändert:  08.07.2010
Kategorie: WPF, .NET-Version: 4.0, [Download]

Der Artikel zeigt eine "sichere" Nutzung von Strings über  Lamda-Ausdrücke mit  INotifyPropertyChanged Implementierung im ViewModel. Dennoch können sie die Methodik auch in vielen anderen Szenarien, wo Strings auftauchen, vorteilhaft einsetzen. Der Vorteil ist u.a., dass nun falsch eingegebene Strings ggf. gleich im Compiler gemeldet werden, was sonst vielleicht erst beim Kunden passiert ist ;-)

  public class Person : InpcBase
  {
    private string vorname = "";
    public string Vorname
    {
      get { return vorname; }
      set
      {
        vorname = value;
        this.RaisePropertyChanged( ()=>Vorname); // Vorteil! Keine Strings mehr!       
} } private string nachname = ""; public string Nachname { get { return nachname; } set { nachname = value; this.RaisePropertyChanged( ()=>Nachname); } } }

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace INPC
{
  /// <summary>Basis-Klasse, die INotifyPropertyChanged 
  /// Basis-Funktionalität zur Verfügung stellt.</summary>
  public class InpcBase : INotifyPropertyChanged
  {
    /// <summary>
    /// Wird geworfen, wenn eine Eigenschaft des Objektes einen neuen Wert bekommt.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary> Wirft das PropertyChanged-Ereignis dieses Objektes.</summary>
    /// <param name="eigenschaftsName">Der Name der Eigenschaft, die den neuen Wert hat.</param>
    protected virtual void RaisePropertyChanged(string eigenschaftsName)
    {
      PropertyChangedEventHandler handler = this.PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(eigenschaftsName));
      }
    }

    /// <summary> Wirft das PropertyChanged-Ereignis dieses 
    ///   Objektes für jede Eigenschaft aus propertyNames.</summary>
    /// <param name="eigenschaftsNamen">Der Namensliste der Eigenschaften, die einen neuen Wert haben.</param>
    protected void RaisePropertyChanged(params string[] eigenschaftsNamen)
    {
      foreach (var name in eigenschaftsNamen)
      {
        this.RaisePropertyChanged(name);
      }
    }

    /// <summary> Wirft das PropertyChanged-Ereignis dieses Objektes.</summary>
    /// <typeparam name="T">Der Typ der Eigenschaft, die den neuen Wert hat.</typeparam>
    /// <param name="eigenschaftsLamdaAusdruck">Ein Lambda-Ausdruck, der 
    ///   Eigenschaft, die einen neuen Wert hat.</param>
    protected void RaisePropertyChanged<T>(Expression<Func<T>> eigenschaftsLamdaAusdruck)
    {
      var propertyName = ExtractPropertyName(eigenschaftsLamdaAusdruck);
      this.RaisePropertyChanged(propertyName);
    }

    private string ExtractPropertyName<T>(Expression<Func<T>> eigenschaftsAusdruck)
    {
      if (eigenschaftsAusdruck == null)
      {
        throw new ArgumentNullException("eigenschaftsAusdruck");
      }

      var memberExpression = eigenschaftsAusdruck.Body as MemberExpression;
      if (memberExpression == null)
      {
        throw new ArgumentException("Der Ausdruck ist kein Member-Lamda-Ausdruck (MemberExpression).", "eigenschaftsAusdruck");
      }

      var property = memberExpression.Member as PropertyInfo;
      if (property == null)
      {
        throw new ArgumentException("Der Member-Ausdruck greift nicht auf eine Eigenschaft zu.", "eigenschaftsAusdruck");
      }

      if (!property.DeclaringType.IsAssignableFrom(this.GetType()))
      {
        throw new ArgumentException("Die referenzierte Eigenschaft gehört nicht zum gewünschten Typ.", "eigenschaftsAusdruck");
      }

      var getMethod = property.GetGetMethod(true);
      if (getMethod == null)
      {
        // das sollte nicht auftreten ...
        throw new ArgumentException("Die referenzierte Eigenschaft hat keine 'get' - Methode.", "eigenschaftsAusdruck");
      }

      if (getMethod.IsStatic)
      {
        throw new ArgumentException("Die refrenzierte Eigenschaft ist statisch.", "eigenschaftsAusdruck");
      }

      return memberExpression.Member.Name;
    }
  }
}