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.
精彩评论