Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF 4 : PropertyChanged not updating Binding

Tags:

c#

binding

wpf

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.

like image 430
Andrew Hanlon Avatar asked Apr 05 '11 12:04

Andrew Hanlon


1 Answers

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) => {  }));
}
like image 88
Alex Zhevzhik Avatar answered Nov 08 '22 14:11

Alex Zhevzhik