Dohvat imena propertya pomoću LINQ izraza
August 17. 2009
0 Comments
- Posted in:
- Quick thoughts
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!