Implementace INotifyPropertyChanged jako BindableBase


Ve WPF, Silverlightu, Metro i WP7 aplikacích je nutné informovat o změnách hodnot objektů, nejčastěji v modelech, nebo v závislosti na použitém návrhovém vzoru i ViewModelech, Presenterech, prostě všude tam, kde je využit Binding a protože implementace rozhraní INotifyPropertyChanged je poměrně dost volná, tak aby nedocházelo ke zbytečnému opakování kódu, je vhodné vytvořit jednu základní třídu právě pro implementaci rozhraní INotifyPropertyChanged a tu pak dále dědit na zmiňovaných místech…

Součástí vzorových Metro aplikací ve Visual Studiu 2012 je jedna zajímavá třída ve vygenerovaném adresáři Common – BindableBase. Ono k podobnému řešení by každý dřív nebo později dospěl, ale proč ho nepoužívat hned… :) V podstatě se jedná o čistou implementaci již zmiňovaného rozhraní INotifyPropertyChanged a třída se tedy skládá z veřejné události PropertyChanged a navíc obsahuje další 2 metody, jednu čistě pro vyvolání události (OnPropertyChanged) a druhou pro nastavení nové hodnoty property včetně kontroly na “duplicitu” a až pak případného vyvolání události (SetProperty<T>). A protože Metro aplikace jsou v .NET Framewroku 4.5, tak se zde již využívá jedna z nových záležitostí jazyka C# 5.0 – CallerMemberName atribut:


  [WebHostHidden]
  public abstract class BindableBase : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;


    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
      if (Equals(storage, value))
        return false;
      storage = value;
      OnPropertyChanged(propertyName);
      return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
      PropertyChangedEventHandler eventHandler = PropertyChanged;
      if (eventHandler == null)
        return;
      eventHandler(this, new PropertyChangedEventArgs(propertyName));
    }
  }

CallerMemberName atribut umožňuje získat jméno metody nebo property z volané metody, tudíž není potřeba tento parametr přímo vyplňovat v případě volání z daného místa:


  public class Person : BindableBase
  {
    private string _FirstName;
    private string _LastName;


    public string FirstName
    {
      get { return _FirstName; }
      set { SetProperty(ref _FirstName, value); }
    }

    public string LastName
    {
      get { return _LastName; }
      set { SetProperty(ref _LastName, value, "LastName"); }
    }
  }

Třída Person dědí od BindableBase a v setrech FirstName a LastName tedy již využívá pro nastavení hodnot poděděnou metodu SetProperty<T>, která je v prvním případě pro FirstName volána bez zadání třetího (volitelného) parametru s názvem property a v druhém případě pro LastName název property zadaný má. Výsledek bude v obou případech stejný, ale protože je v metodě SetProperty<T> pro třetí parametr použitý právě atribut CallerMemberName, tak zde není potřeba název explicitně zadávat. To by mělo smysl pouze pokud bychom chtěli nastavit hodnotu z jiného umístění než přímo z property.

, , , ,

Komentáře jsou uzavřeny.