0 Comments
LINQPad Jedna od glavnih značajki Silverlight i WPF aplikacija je implementacija INotifyPropertyChanged interfacea, pomoću kojega se obavještava da je promijenjena vrijednost nekog property-a. Jedan tipični primjer "pretplatnika" na takve obavijesti je sučelje aplikacije; ukoliko se deklarativno u XAMLu poveže klasa, odnosno njen property za neki UI element, prilikom promjene vrijednosti propertya unutar te klase sučelje će automatski ispisati promijenjene vrijednosti. U propertyu klase kod set operacije moramo podignuti event i obavijestiti sve zainteresirane da je došlo do promjene :
public string PropertyName
{
    get { return _propertyName;  }
    set { 
        _propertyName=value; 
        NotifyPropertyChanged("PropertyName"); 
    }
}
Metoda NotifyPropertyChanged i parametar u obliku stringa je standardni način obavještavanja o promjeni vrijednosti. Takav način upotrebe "Magic stringova" je podložan bugovima, jer zamislimo da promijenimo naziv samog property-a ali ne i string unutar poziva metode NotifyPropertyChanged. Kompajler bi uredno obavio svoj posao bez dojave greške, a aplikacija bi se počela čudno ponašati. Baš zbog toga ja želim ovako pisati podizanje eventa:
public string PropertyName
{
    get { return _propertyName;  }
    set { 
        _propertyName=value; 
        this.RaisePropertyChanged(x=>x.PropertyName); 
    }
}
Ovo je c# 3.0 izraz (Expression), s kojim dobijemo potpuni Intellisense i compile time provjeru. Metoda RaisePropertyChanged je sada izvedena kao extension metoda. Cijeli kod ovako izgleda:
public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public static class ViewModelExtension
{
    public static void RaisePropertyChanged<T , TProperty>(this T observableBase, Expression<Func<T, TProperty>> expression) where T : ViewModelBase
    {
        observableBase.RaisePropertyChanged( observableBase.GetPropertyName(expression) );
    }

    public static string GetPropertyName<T , TProperty>(this T owner, Expression<Func<T, TProperty>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        if (memberExpression == null)
        {
            var unaryExpression = expression.Body as UnaryExpression;
            if (unaryExpression != null)
            {
                memberExpression = unaryExpression.Operand as MemberExpression;
                if (memberExpression == null)
                    throw new NotImplementedException();
            }
            else
                throw new NotImplementedException();
        }
        var propertyName = memberExpression.Member.Name;
        return propertyName;
    }
}
ViewModelBase je apstraktna klasa koju mora nasljeđivati klasa koja je bindana na sučelje. Ovaj kod sam preuzeo iz Silverlight MVVM toolkita, jer je puno ljepši od moje implementacije :) Ali glavna značajka je da se zasniva na izrazu Func<T, TProperty>, preko kojega se dođe do tijela izraza, odnosno naziva propertya (expression.Body, castano u MemberExpression, i onda Member.Name metoda tog MemberExpression-a, koja pomoću refleksija čita sam naziv propertya!).

Ovaj kod se može upotrijebiti u bilo kojem projektu gdje se mora čitati naziv propertya, samo se promjeni uvjet where T: ViewModelBase u neki drugi baznu klasu, ili pak makne cijeli taj uvjet!