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!