Compartilhar via


Bubbling PropertyChanged Events in Silverlight

I’ve been working with Silverlight (specifically for Windows Phone 7) recently and something I’ve found useful is the ability to “bubble” PropertyChanged event up. So for example;

    1:      public class MainPageViewModel : INotifyPropertyChanged
    2:      {
    3:          public ObservableCollection<Person> People
    4:          {
    5:              get
    6:              {
    7:                  return  _repository.Items;
    8:              }
    9:          }
   10:   
   11:          // ... other members ommitted
   12:      }

In this situation I have a repository of Person objects, and that repository implements INotifyPropertyChanged. But I don’t want to expose the repository as part of my View Model because that defeats the point of my encapsulation!

Now, if the ObservableCollection contents change, my View will get notified via the CollectionChanged event and will update itself. But if the repository destroys and replaces the whole collection, the View will never be notified.

As a solution to this, I created a very simple class, as follows;

    1:      public class NotifyPropertyChangedBubbler
   2:      {
   3:          private INotifyPropertyChanged _source;
   4:          private PropertyMap[] _propertyNames;
   5:          private Action<string> _target;
   6:   
   7:          public NotifyPropertyChangedBubbler(
   8:              INotifyPropertyChanged source, 
   9:              PropertyMap[] propertyNames,
  10:              Action<string> target)
  11:          {
  12:              _source = source;
  13:              _propertyNames = propertyNames;
  14:              _target = target;
  15:   
  16:              _source.PropertyChanged += 
  17:                  new PropertyChangedEventHandler(PropertyChanged);
  18:          }
  19:   
  20:          private void PropertyChanged(object sender, PropertyChangedEventArgs e)
  21:          {
  22:              var map = _propertyNames.FirstOrDefault(
  23:                  pm => pm.Source == e.PropertyName);
  24:              if (map != null)
  25:              {
  26:                  _target(map.Target);
  27:              }
  28:          }
  29:      }

And in my View Model I use it like this;

    1:      public MainPageViewModel()
    2:      {
    3:          var maps = new[] 
    4:          {
    5:              new PropertyMap("Items", "People")
    6:          };
    7:          _bubbler = new NotifyPropertyChangedBubbler(
    8:              _repository, maps, OnPropertyChanged);
    9:      }

 You’ll notice I can map one property name to another – because on my Repository the collection is named Items, but it is exposed through my View Model as People. The last argument to the bubbler’s constructor is the current class’ OnPropertyChanged function, which in turn raises the event.

This seems to work quite well. Does anyone have another approach? I feel like there should be a less-code solution, so I’m open to suggestions.

Comments

  • Anonymous
    October 28, 2010
    I've tackled this problem, too (it's like we're on the same wavelength!). My solution looks at what your code is doing and figures out what it depends upon. Then it fires PropertyChanged events from the View Model whenever that data changes. So the code you write in the View Model is just the property: public IEnumerable<Person> People {  get { return _model.People; } } In the Model, you use a class called Independent to capture gets and sets: private List<Person> _people = new List<Person>(); private Independent _indPeople = new Independent(); public IEnumerable<Person> People {  get { _indPeople.OnGet(); return _people; } } public void AddPerson(Person p) {  _indPeople.OnSet();  _people.Add(p); } This project is also on Codeplex. updatecontrols.codeplex.com