Search code examples
wpfbindingpropertychangedupdatesourcetrigger

A weird behavior of PropertyChanged UpdataSourceTrigger in WPF


I have an entity like this:

public class Person
{
    public string Name { get; set; }

    public Person()
    {
        Name = "Godspeed";
    }
}

Then I have three textbox and a button in XAML:

<Window x:Class="WpfApplication19.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication19"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:Person />
    </Window.DataContext>
    <StackPanel>
        <TextBox Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}" />
        <Button Click="Button_Click">Click</Button>
    </StackPanel>
</Window>

The weird thing is that, the entity "Person" doesn't implement the INotifyPropertyChanged, but when one text box is changed, it modifies the source(Person object), but we didn't raised the property changed event but the rest two textboxes automatically changed.

When the button clicks, we update the source directly by code like:

((Person)DataContext).Name = "Godspeed";

It doesn't update. So what I think is that if the Person class implement the INotifyPropertyChanged, this behavior is normal, but now the class doesn't implement the interface, but it update the interface too. Please info me the reason if you have some clue. Thanks.


Solution

  • The reason is PropertyDescriptor, see the following thread, the same question is being asked: How does the data binding system know when a property is changed?

    Here is two of the answers

    I think the magic lies in the binding system's use of PropertyDescriptor (SetValue presumably raises a ValueChanged - the PropertyDescriptor is likely shared, while the events are raised on a per-object basis).


    I'm not at Microsoft, but I can confirm it. If PropertyDescriptor is used to update the value, as it will be, then relevant change notifications are automatically propagated.

    Edit
    You can verify this by naming the Person DataContext object

    <Window.DataContext>
        <local:Person x:Name="person"/>
    </Window.DataContext>
    

    and add the following code to the MainWindow ctor

    public MainWindow()
    {
        InitializeComponent();
    
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(person);
        PropertyDescriptor nameProperty = properties[0];
        nameProperty.AddValueChanged(person, OnPropertyChanged);
    }
    void OnPropertyChanged(object sender, EventArgs e)
    {
        MessageBox.Show("Name Changed");
    }
    

    Once you change the value on any of the three TextBoxes, you'll end up in the event handler OnPropertyChanged.