14

Let's say in some abstract ViewModel base-class I have a plain-old property as follows:

public Size Size
{
    get { return _size; }
    set
    {
        _size = value;
        OnPropertyChanged("Size");
    }
}

I then create a more specific ViewModel, inheriting from the previous one, which contains the following property:

public Rect Rectangle
{
    get { return new Rect(0, 0, _size.Width, _size.Height); }
}

Now, in some View class I bind to the aforementioned ViewModel's Rectangle property. Everything works fine, until I change the size. When Size changes, Rectangle doesn't know about it and the change doesn't propagate to the View. And since Rectangle is in the child class, I can't simply add an OnPropertyChanged("Rectangle") to the Size setter.

Now imagine that I have many different properties like Rectangle, that all depend on base-class properties, and that none of these changes are being propagated. I need some lightweight and elegant way of chaining change notifications, preferably one that doesn't require a lot of code and doesn't force me into using dependency properties.

Obviously there are a lot of ugly solutions here- what I am looking for is something clean and clever. It seems to me this would be a very common scenario, and it seems to me there might be an MVVM-friendly way of doing this.

Charlie
  • 15,069
  • 3
  • 64
  • 70

5 Answers5

9

I recently blogged about this exact problem. I include a [DependsUpon("Size")] attribute with the Rectangle. I REALLY like this approach, because it keeps the dependency knowledge with the code that creates the dependency, not the other way around.

Take a look: http://houseofbilz.com/archive/2009/11/14/adventures-in-mvvm----dependant-properties-with-inotifypropertychanged.aspx

Brian Genisio
  • 47,787
  • 16
  • 124
  • 167
  • Great idea. I changed it a bit to create a dictionary at construction time (for performance reasons), but the concept is sound. – Charlie Jan 06 '10 at 18:13
  • The link is broken. Does anyone have an example of a good way to implement this? – Henry Crans Mar 20 '19 at 17:33
  • Sorry about that. I have an archive of that post here. It's 10 years outdated, so I'm not sure at all if this info is still relevant, but here's the archived text: https://github.com/BrianGenisio/briangenisio.github.com/blob/houseofbilz-migration/source/_posts/2009-11-14-adventures-in-mvvm-dependant-properties-with-inotifypropertychanged.md – Brian Genisio Mar 21 '19 at 18:28
4

I use Josh Smith's PropertyObserver, which you can get from his MVVM Foundation library at http://mvvmfoundation.codeplex.com/.

Usage:

_viewmodel_observer = new PropertyObserver<OtherViewModel>(_OtherViewModel)
   .RegisterHandler(m => m.Size, m => RaisePropertyChanged(Rectangle);

Brian's attribute approach is nice too. One thing I like about PropertyObserver is that I can execute arbitrary code; allowing me to check conditions which may make me avoid the raise or perform other actions all together.

Thomas
  • 3,348
  • 4
  • 35
  • 49
3

You can simply override OnPropertyChanged in the derived ViewModel like so:

protected override void OnPropertyChanged(string propertyName) {
    base.OnPropertyChanged(propertyName);
    if (propertyName == "Size") {
        base.OnPropertyChanged("Rectangle");
    }
}

Another possibility... A while back I put together a pretty nice ViewModel base class that supports attributes on properties like:

[DependsOn("Size")]
public Rect Rectangle {
    get { new Rect(0,0,Size.Width, Size.Height); }
}

Then the ViewModel base class collects these DependsOnAttribute's at runtime and in its OnPropertyChanged method it basically just looks to see what other properties need to be invalidated when a property change occurs.

Josh
  • 68,005
  • 14
  • 144
  • 156
  • Overriding OnPropertyChanged is definitely a solution, but not an elegant one. I like your second suggestion better, though. – Charlie Jan 06 '10 at 15:54
  • Not all solutions call for elegance. Overriding OnPropertyChanged is very straightforward and I still do that in many cases unless the dependencies get hairy which led me to do the 2nd approach. – Josh Jan 06 '10 at 16:51
  • You are right, but remember in my question I did specifically say, "Now imagine that I have many different properties like Rectangle, that all depend on base-class properties, and that none of these changes are being propagated." So the assumption is that the hierarchy is already getting hairy. – Charlie Jan 06 '10 at 18:16
  • To be honest, I'm not completely satisfied with my attribute-based approach either. I have been toying with a DSL to create ViewModels that picks up on which properties should trigger dependent property changes but I haven't gotten that to anywhere near a satisfactory point. – Josh Jan 07 '10 at 00:11
1

A clean MVVM way would be to use a Messenger subscribe/notify mechanism (like in Josh Smith's MvvmFoundation)

Create a singleton Messenger object somewhere - the main App class is always a good place for this

public partial class App : Application
{
    private static Messenger _messenger;
    public static Messenger Messenger
    {
        get
        {
            if (_messenger == null)
            {
                _messenger = new Messenger();
            }
            return _messenger;
        }
    }
}

In the Size setter from the base class, notify changes:

public Size Size
{
    get { return _size; }
    set
    {
        _size = value;
        OnPropertyChanged("Size");

        App.Messenger.NotifyColleagues("SIZE_CHANGED");
    }
}

Now you can let your inherited ViewModel's listen for these changes, and raise PropertyChanged events as appropriate...

public MyViewModel : MyViewModelBase
{
    public MyViewModel()
    {
        App.Messenger.Register("SIZE_CHANGED", () => OnPropertyChanged("Rectangle"));
    }
}

Of course - you can add as many subscriptions to this message as you need - one for each property that needs changes to be notified back to the View...

Hope this helps :)

kiwipom
  • 7,639
  • 37
  • 37
  • This just does exactly the same as overriding OnPropertyChanged for the child in the parent, not sure it really makes it any cleaner – AwkwardCoder Jan 06 '10 at 11:21
  • This isn't a bad idea at all, but I want something a little more lightweight. – Charlie Jan 06 '10 at 18:14
  • The benefit of using a Messenger approach may be in cases where you have deeply nested hierarchies where it would be inconvenient and perhaps inefficient to chain the property change events. – jpierson Nov 11 '12 at 04:09
0

maybe because im a VB guy, but in your Rectangle code it looks like you are accessing the private _size declaration instead of the Public Size property which would not fire the OnPropertyChanged event to alert the view.

Also I may be off base, but shouldnt Rectangle be an actual Object while Size is a property of that object? Maybe that is what you are doing..some C# methodologies are still really foreign to me.

ecathell
  • 1,030
  • 13
  • 25
  • I am using the private _size declaration but it's irrelevant. That is a getter, not a setter. The view needs to be notified that Rectangle is changing when Size changes, not when Rectangle is being accessed. – Charlie Jan 06 '10 at 15:56