Należy wyjaśnić tę kwestię gdyż jest ona bardzo istotna i będę jej używał podczas dalszego opisywania moich bojów z "Binding" :).
Cofnijmy się więc do definicji klasy "ViewModel'u".
public class MainWindowViewModel { private string _name = string.Empty; public string Name { get { return _name; } set { _name = value; } } }Gdy właściwość "Name" zdefiniowana jak powyżej, zostanie zmieniona zmiana ta nie będzie odwzorowana na widoku.
Co w takim razie należy zrobić?
Przecież właśnie o to chodzi, żeby zmiany w "ViewModel'u" były odwzorowane na widoku!!!
Nie ma się co denerwować, rozwiązanie oczywiście jest. :)
Należy ogłosić wszem i wobec, że wartość właściwości "Name" została zmieniona, użyjemy do tego interfejsu "INotifyPropertyChanged".
W swoim kodzie używam klasy bazowej implementującej ten interfejs, po której dziedziczę klasy wymagające jego implementacji.
Zaczynam od implementacji zdarzenia "PropertyChanged" z interfejsu.
Poniższa implementacja odporna jest na wywołania z różnych wątków, zapewnia nam to "lock".
object _objectLock = new object(); event PropertyChangedEventHandler PropertyChangedEvent; public event PropertyChangedEventHandler PropertyChanged { add { lock (_objectLock) { PropertyChangedEvent += value; } } remove { lock (_objectLock) { PropertyChangedEvent -= value; } } } private void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChangedEventHandler handler; lock (_objectLock) { handler = PropertyChangedEvent; } if (handler != null) handler(this, e); }Następnie definiujemy metodę, która ma być wywoływana po zmianie wartości właściwości.
protected void FirePropertyChanged(string propertyName) { OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); }Modyfikacja właściwości "Name" z wywołaniem zdarzenia wygląda tak:
public string Name { get { return _name; } set { _name = value; FirePropertyChanged("Name"); } }Niestety takie wywołanie nie jest odporne na zmianę nazwy właściwości "Name", dla mnie dodatkowo jest mało estetyczne.
Dużo wygodniej było by przekazać jako parametr samą właściwość "Name".
public string Name { get { return _name; } set { _name = value; FirePropertyChanged(() => Name); } }Aby uzyskać możliwość takiego wywoływanie zdarzenia należy dodać metodę:
protected void FirePropertyChanged<T>(Expression<Func<T>> propertyExpresssion) { FirePropertyChanged(Helper.GetPropertyName(propertyExpresssion)); }Dodatkowo należy też dodać metodę "GetPropertyName" przekształcającą wyrażenie zawierające instancję właściwości w nazwę tej właściwości.
Oczywiście i takie rozwiązanie dla was przygotowałem :D
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpresssion) { if (propertyExpresssion == null) throw new ArgumentNullException("propertyExpresssion"); var memberExpression = propertyExpresssion.Body as MemberExpression; if (memberExpression == null) throw new ArgumentException("The expression is not a member access expression."); var property = memberExpression.Member as PropertyInfo; if (property == null) throw new ArgumentException("The member access expression does not access a property."); var getMethod = property.GetGetMethod(true); if (getMethod.IsStatic) throw new ArgumentException("The referenced property is a static property."); return memberExpression.Member.Name; }Podsumowując cały wpis, zamieszczam pełen kod klasy "ObservableItem":
public class ObservableItem : INotifyPropertyChanged { object _objectLock = new object(); event PropertyChangedEventHandler PropertyChangedEvent; public event PropertyChangedEventHandler PropertyChanged { add { lock (_objectLock) { PropertyChangedEvent += value; } } remove { lock (_objectLock) { PropertyChangedEvent -= value; } } } private void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChangedEventHandler handler; lock (_objectLock) { handler = PropertyChangedEvent; } if (handler != null) handler(this, e); } protected void FirePropertyChanged<T>(Expression<Func<T>> propertyExpresssion) { FirePropertyChanged(Helper.GetPropertyName(propertyExpresssion)); } protected void FirePropertyChanged(string propertyName) { OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } public static class Helper { public static string GetPropertyName<T>(Expression<Func<T>> propertyExpresssion) { if (propertyExpresssion == null) throw new ArgumentNullException("propertyExpresssion"); var memberExpression = propertyExpresssion.Body as MemberExpression; if (memberExpression == null) throw new ArgumentException("The expression is not a member access expression."); var property = memberExpression.Member as PropertyInfo; if (property == null) throw new ArgumentException("The member access expression does not access a property."); var getMethod = property.GetGetMethod(true); if (getMethod.IsStatic) throw new ArgumentException("The referenced property is a static property."); return memberExpression.Member.Name; } } }
Jeszcze chciałem wspomnieć, że jeśli potrzebujemy kolekcję i chcielibyśmy obserwować kiedy liczba jej elementów zostanie zmieniona to najlepszym rozwiązaniem jest użycie "ObservableCollection".
Brak komentarzy:
Prześlij komentarz