Invest in people before investing in tools

Il blog di Matteo Baglini
posts - 118, comments - 95, trackbacks - 697

venerdì 28 novembre 2008

Pattern Model-View-ViewModel, INotifyPropertyChanged, Static Reflection e Extension methods

Anch’io nei miei progetti WPF per rendere testabile la logica della mia applicazione senza rinunciare alla pontenza del DataBinding utilizzo (anche) il Pattern Model-View-ViewModel. L’interfaccia INotifyPropertyChanged gioca un ruolo fondamentale nell’implementazione di questo pattern quindi ne faccio un uso massiccio. Quello che non mi è mai piaciuto di questa interfaccia è l’uso delle stringe per indicare qual è la proprietà modificata, vediamo un semplice esempio dell’uso di INotifyPropertyChanged:

   1:  public class Person : INotifyPropertyChanged {
   2:    public event PropertyChangedEventHandler PropertyChanged;
   3:    
   4:    private string firstName = String.Empty;
   5:    public string FirstName {
   6:      get { return firstName; }
   7:      set {
   8:        if (firstName != value) {
   9:          firstName = value;
  10:          RaisePropertyChanged("FirstName");
  11:        }
  12:      }
  13:    }
  14:   
  15:    private void RaisePropertyChanged(string propertyName) {
  16:      if (PropertyChanged != null)
  17:        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  18:    }
  19:  }

Se cambio il nome della proprietà FirstName Visual Studio si accorge del cambiamento e mi permette con un solo click di rinominare la proprietà ovunque venga utilizzata, però questo sistema di refactoring non è abbastanza smart da modificare la stringa FirstName (riga 10). Grazie alla Static Reflection possiamo eliminare questo problema, il “trucco” sta nel passare al metodo RaisePropertyChanged la proprietà come tipo Expression<Func<T>>, convertire successivamente il Body nel tipo MemberExpression ed in fine recuperare il nome tramite le proprietà Member. Risultato:

   1:  public class Person : INotifyPropertyChanged {
   2:    public event PropertyChangedEventHandler PropertyChanged;
   3:    
   4:    private string firstName = String.Empty;
   5:    public string FirstName {
   6:      get { return firstName; }
   7:      set {
   8:        if (firstName != value) {
   9:          firstName = value;
  10:          RaisePropertyChanged(() => FirstName);
  11:        }
  12:      }
  13:    }
  14:   
  15:    private void RaisePropertyChanged<T>(Expression<Func<T>> property) {  
  16:      var expression = property.Body as MemberExpression;
  17:      var member = expression.Member;
  18:      if (PropertyChanged != null)
  19:        PropertyChanged(this, new PropertyChangedEventArgs(member.Name));
  20:    }
  21:  }

Adesso il nostro codice supporta pienamente il rename refactoring. Come ultimo passo possiamo incapsulare la logica di manipolazione dei tipi Expression in un Extension methods rendendo il codice molto più leggibile:

   1:  public static class PropertyExtensions {
   2:    public static PropertyChangedEventArgs CreateChangeEventArgs<T>(this Expression<Func<T>> property) {
   3:      var expression = property.Body as MemberExpression;
   4:      var member = expression.Member;
   5:      return new PropertyChangedEventArgs(member.Name);
   6:    }
   7:  }
   8:   
   9:  public class Person : INotifyPropertyChanged {
  10:    public event PropertyChangedEventHandler PropertyChanged;
  11:    
  12:    private string firstName = String.Empty;
  13:    public string FirstName {
  14:      get { return firstName; }
  15:      set {
  16:        if (firstName != value) {
  17:          firstName = value;
  18:          RaisePropertyChanged(() => FirstName);
  19:        }
  20:      }
  21:    }
  22:   
  23:    private void RaisePropertyChanged<T>(Expression<Func<T>> property) {  
  24:      if (PropertyChanged != null)
  25:        PropertyChanged(this, property.CreateChangeEventArgs());
  26:    }
  27:  }

posted @ lunedì 1 gennaio 0001 00:00 | Feedback (13) | Filed Under [ .NET ]

Powered by:
Powered By Subtext Powered By ASP.NET