views:

114

answers:

2

The XAML {Binding} construct is very handy because it handles all of the PropertyChanged issues automatically. It is really impressive when you hand it a path to an object, through .NET data structures, and everything is automatically updated for you.

I would like to use the same thing in C#. I would like to have a property that is derived from the value of another property. Example

class Foo
{
    public Bar Bar = new Bar();
    public string ItGetter
    {
        get
        {
            return Bar.Baz.It;
        }
    }
}

class Bar
{
    public Baz Baz = new Baz();
}

class Baz
{
    public string It { get { return "You got It!"; } }
}

If you call ItGetter on a Foo, you get the It value from Baz. This works fine, except that it is not invalidated--i.e., if It changed, there would be no change notifications on the ItGetter. Furthermore, if the Foo.Bar or Bar.Baz references are changed, you would also not get change noficiations.

I can add the appropriate IChangeNotify code on the properties, but my question is: How do I code the ItGetter property such that it will call its PropertyChanged event when any of the references in the path, or the It value change? I'm hoping I don't have to manually setup property changed events on all the items in the path....

Thanks for any help!

Eric

+1  A: 

You could take a look at dependancy properties. They allow you to define properties in the WPF property system that are backed with stacks of metadata and a detailed value resolution system.

Importantly for you they allow they allow you to register for property changed events, and they allow you to make values dependant on other stuff.

There are some other good artcile around such as 'Demystifying dependency properties' by Josh Smith and 'Dependency Properties' by Christian Mosers

You might also want to read Dependency Property Callbacks and Validation

Simon P Stevens
See my answer (Eric), below for the full code of this solution.
Eric
+1  A: 

Here is the full code that does what I was looking for, using Dependency Properties, as Simon mentioned:

// This class exists to encapsulate the INotifyPropertyChanged requirements
public class ChangeNotifyBase : DependencyObject, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
}

public class Foo : ChangeNotifyBase
{
    public Foo()
    {
        Bar = new Bar();
        var binding = new Binding("Bar.Baz.It");
        binding.Source = this;
        binding.Mode = BindingMode.TwoWay;
        BindingOperations.SetBinding(this, ItGetterProperty, binding);
    }

    /// <summary>
    /// The ItGetter dependency property.
    /// </summary>
    public bool ItGetter
    {
        get { return (bool)GetValue(ItGetterProperty); }
        set { SetValue(ItGetterProperty, value); }
    }
    public static readonly DependencyProperty ItGetterProperty =
        DependencyProperty.Register("ItGetter", typeof(bool), typeof(Foo));

    // Must do the OnPropertyChanged to notify the dependency machinery of changes.
    private Bar _bar;
    public Bar Bar { get { return _bar; } set { _bar = value; OnPropertyChanged("Bar"); } }
}

public class Bar : ChangeNotifyBase
{
    public Bar()
    {
        Baz = new Baz();
    }
    private Baz _baz;
    public Baz Baz { get { return _baz; } set { _baz = value; OnPropertyChanged("Baz"); } }
}

public class Baz : ChangeNotifyBase
{
    private bool _it;
    public bool It { get { return _it; } set { _it = value; OnPropertyChanged("It"); } }
}

If you now register for events on ItGetter, you will get notified if any of these things change: Baz.It Foo.Bar (I.e., change the reference) Bar.Baz " "

If you set on of the object references (Foo.Bar or Bar.Baz) to null, the value of ItGetter changes to false.

Eric
Nice one Eric +1. It's great to see someone who has taken the time to come back and contribute their results. This is really in the spirit of SO, Thanks.
Simon P Stevens