niedziela, 27 listopada 2011

MVVM cz.1 - INotifyPropertyChanged

Interfejs INotifyPropertyChangedPropertyChanged znany jest prawdopodobnie każdemu programiście C#. Interfejs ten zawiera jedynie jedno zdarzenie

public event PropertyChangedEventHandler PropertyChanged;

które powinniśmy odpalić w momencie gdy dana właściwość zostanie przez nas zmieniona. Dzięki temu dowolny obiekt, który będzie nasłuchiwał zmian danej właściwości zostanie o tym poinformowany. Przykładowa klasa implementująca ten interfejs może wyglądać w następujący sposób:


public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

private string viewModelName;
public string ViewModelName
{
get { return viewModelName; }
set
{
viewModelName = value;
NotifyPropertyChanged("ViewModelName");
}
}
}

Jak widać została stworzona funkcja pomocniczna

public void NotifyPropertyChanged(string propertyName)

którą wywołujemy w setterze danej właściwości, podając w parametrze nazwę tej właściwości.
Niestety podejście takie jest bardzo podatne na błędy. Po pierwsze bardzo łatwo można przekręcić nazwę właściwości, a co gorsze rozwiązanie takie nie jest uwzględniane podczas refactoringu. Dlatego też w celu poprawienia naszego rozwiązania skorzystamy z mechanizmów dostarczanych przez Expression Trees.
Po pierwsze zmieniamy funkcję

public void NotifyPropertyChanged(string propertyName)

na

public void NotifyPropertyChanged(Expression<Func<object>> propertyExpression)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(GetPropertyName(propertyExpression)));
}

funkcja pomocnicza GetPropertyName ma następującą postać

private string GetPropertyName(Expression<Func<object>> propertyExpression)
{
var unaryExpression = propertyExpression.Body as UnaryExpression;
var memberExpression = unaryExpression == null ? (MemberExpression)propertyExpression.Body : (MemberExpression)unaryExpression.Operand;
var propertyName = memberExpression.Member.Name;
return propertyName;
}

Teraz wystarczy prosty zapis

NotifyPropertyChanged(()=> ViewModelName)

który odwołuje się bezpośrednio do właściwości a nie jej nazwy. Dzięki temu kod w całości zostanie zrefaktoryzowany. Rozwiązanie to jest teoretycznie odrobinę wolniejsze od używania stringów, jednak w praktyce jest to niezauważalne.
Można również funkcję

string GetPropertyName(Expression<Func<object>> propertyExpression)

wyciągnąc do osobnej klasy i zrobić z tej funkcji extension method.

public static string GetPropertyName(this object obj, Expression<Func<object>> propertyExpression)
{
var unaryExpression = propertyExpression.Body as UnaryExpression;
var memberExpression = unaryExpression == null ? (MemberExpression)propertyExpression.Body : (MemberExpression)unaryExpression.Operand;
var propertyName = memberExpression.Member.Name;
return propertyName;
}

Od teraz wystarczy, że zaimportujemy odpowiedni namespace i możemy wykorzystać naszą funkcję w każdej klasie.

3 komentarze:

Anonimowy pisze...

public void NotifyPropertyChanged(Expression> propertyExpression)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(GetPropertyName(propertyName)));
}

hmm... propertyName na propertyExpression? ;->

Tomek pisze...

poprawione, dzięki

Anonimowy pisze...

Mam następujące pytanie:
Czy implementacja interfejsu INotifyPropertyChanged za pomocą metody korzystającej z atrybutu CallerMemberNameAttribute jest równoznaczna z powyższą implementacją?
Przykład:
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged
([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEvenArgs
(propertyName));
}
}
I oczywiście metodę NotifyPropertyChanged() odpalamy w naszych setterach. Impelemnatcja dużo prostsza ale czy jednoznaczna?

Prześlij komentarz

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Online Project management