开发者

Update binding when nested object changes

I have a some XAML as follows (a simple Label and Button):

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Test="clr-namespace:WpfApplication2">
<StackPanel DataContext="{Binding Path=TestPerson}">
    <Label Content="{Binding Path=Name}"></Label>
    <Button Content="Button" Click="button1_Click" />
</StackPanel>
</Window>

in the code behind I have:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private Person _person = new Person();
    public Person TestPerson { get { return _person; } }

    public MainWindow()
    {           
        DataContext = this;
        InitializeComponent();
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        _person.Name = "Bill";
        //_person = new Person() { Name = "Bill" };
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("TestPerson"));
        }
    }

    public event PropertyChangedEventHandler Prop开发者_StackOverflowertyChanged;
}

and the class Person is:

public class Person
{
    string _name = "Bob";

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

As it is, firing the Propertychanged event does not cause the Label's contents to change to Bill.

I've found I am able to overcome this by either:

  • Assigning a new object to _person (as in the commented out line)
  • Removing the DataContext from the StackPanel and have Label bind to Path=TestPerson.Name

I don't understand why I have to actually assign a NEW object to _person for the Label to update, or use the FULL path in the binding.

Is there a way to update the Label without supplying the full path (relying on the DataContext), and without assigning a new object to _person?


You raise PropertyChanged for the Person instance TestPerson. However, TestPerson hasn't changed, it is the Name property of TestPerson that has changed and that is the property the Label is binding to.

Edit: To answer why your first two versions work

Assigning a new object to _person (as in the commented out line)

Here you are actually changing the value of TestPerson and because DataContext is inherited by the children, the Label gets a new DataContext as well so that's why the Binding is updated.

Removing the DataContext from the StackPanel and have Label bind to Path=TestPerson.Name

This is something I've never seen. The same binding subscribes to PropertyChanged for both TestPerson and Name in Person so raising PropertyChanged for any of these properties will work.

If you want to overcome this without implementing INotifyPropertyChanged for Person, you can change set UpdateSourceTrigger to Explicit

<Label Name="label"
       Content="{Binding Path=Name, UpdateSourceTrigger=Explicit}"/>

And update the Binding manually whenever Name changes

private void button1_Click(object sender, RoutedEventArgs e)
{
    _person.Name = "Bill";
    BindingExpression be = label.GetBindingExpression(Label.ContentProperty);
    be.UpdateTarget();
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs("TestPerson"));
    }
}

Otherwise, just implement INotifyPropertyChanged for Person as well and it will work

public class Person : INotifyPropertyChanged
{ 
    string _name = "Bob"; 

    public string Name 
    { 
        get { return _name; } 
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        } 
    } 

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
} 


You need a little change in your XAML...

In your code behind, instead of setting DataContext as this, set it in XAML via Binding...

     <StackPanel DataContext="{Binding RelativeSource={RelativeSource
                                            AncestorType= {x:Type Window},
                                                Mode=FindAncestor}, 
                                       Path=TestPerson}">
          <Label Content="{Binding Path=Name}"></Label>
          <Button Content="Button" Click="button1_Click" />
     </StackPanel>

Remove the

     DataContext = this;

from your code behind.

Let me know if this helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜