5
votes

In my ViewModel, I have a class "A" with a child property "B" that is also a custom class. Both classes implement INotifyPropertyChanged and B's PropertyChanged event is hooked up to fire A's PropertyChanged event (with the correct property name of "B").

I also have a DependencyProperty "DPB" on my ViewModel that is bound to B in code with a very simple binding (new Binding("A.B")).

Now I have three TextBoxes in my view:

  • 1 bound to A.B.C (a property of B)
  • 1 bound directly to A.B
  • 1 bound to DPB

On first run, both A.B and the DPB textboxes show the correct value. But when I change the A.B.C textbox, only the A.B textBox is updated - the DPB textBox is not updated.

I have debugged through all of the PropertyChanged notifying code and they are all hit with the correct values passed.

The problem seems to be that the DependencyProperty (or it's binding) is not being updated when the PropertyChanged event is fired. Can anyone tell me why or how to change this behaviour?

thank you.

1
You may need to add a Mode=TwoWay to the binding. However, I'm not sure I understood correctly your question, could you add some code?Damascus
Would look so much better if you build the class structure a little and paste it's code here...Theun Arbeider

1 Answers

6
votes

I have bad news for you.

Inside DependencyObject.SetValue the check is located, which verify if new value equals to old value. So if you are binded to A.B, and changing of A.B.C produces PropertyChanged event for A.B, Binding mechanizm will handle this event and even call DependencyObject.SetValue. But then (due to equality of old and new A.B values) no changes will be applied to DP.

In order to achieve correct DP fireing, you should create new instance of A.B, that concludes in great headache.

UPDATED

You could use Freezable object, which supports notification that it has changed when its property is changed. DependencyObject works with Freezables correctly, so next example does what you need.

Model classes:

public class A 
{
    public A()
    {
        this.B = new B();
    }
    public B B
    {
        get; private set;
    }
}

public class B : Freezable, INotifyPropertyChanged
{
    protected override Freezable CreateInstanceCore()
    {
        return new B();
    }

    private string _c = "initial string";
    public string C
    {
        get
        {
            return _c;
        }
        set
        {
            this._c = value;
            this.OnPropertyChanged("C");
            this.OnChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        var safe = this.PropertyChanged;
        if (safe != null)
        {
            safe(this, new PropertyChangedEventArgs(name));
        }
    }
}

Xaml:

<StackPanel>
    <TextBox Text="{Binding A.B.C}" />
    <TextBox Text="{Binding MyProperty.C}" />
    <Button Click="Button_Click"/>
</StackPanel>

Code behind:

public partial class TextBoxesView : UserControl
{
    public TextBoxesView()
    {
        InitializeComponent();

        this.A = new A();
        this.DataContext = this;

        BindingOperations.SetBinding(this, TextBoxesView.MyPropertyProperty, new Binding("A.B"));
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.A.B.C = DateTime.Now.ToString();
    }

    public A A
    {
        get;
        private set;
    }

    public B MyProperty
    {
        get
        {
            return (B)this.GetValue(TextBoxesView.MyPropertyProperty);
        }
        set
        {
            this.SetValue(TextBoxesView.MyPropertyProperty, value);
        }
    }

    public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.Register("MyProperty",
            typeof(B),
            typeof(TextBoxesView),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, (d, e) => {  }));
}