开发者

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.


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.


Well, as you said, you just have to implement INotifyPropertyChanged

The PropertyChanged event is used when you set a property from code and need to reflect this change to your UI (UI will cath the PropertyChanged event, and thanks to your UpdateSourceTrigger the UI will be updated). The other side (changing from UI) does not need any PropertyChanged, this is why you get this behavior

Just try it like that:

    public class Person : INotifyPropertyChanged
    {
            #region INotifyPropertyChanged Members

            /// <summary>
            /// Property Changed Event
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;

            /// <summary>
            /// Property Changed
            /// </summary>
            /// <param name="propertyName"></param>
            protected void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion

    private string name;

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

}

Using this code, when you set the Name, the PropertyChanged event will be fired and therefore update UI accordingly :)


It works not only with updatesourcetrigger=propertychanged, but with default (lost focus) value too. In addition to what @Meleak said, I want to point that it is good behaviour. Any changes made by ui are propagated to all binding targets. Binding engine wants to propagate this changes to all controls at once. If you make changes through code, and not implement INotifyPropertyChanged - changes made from code are not reflected at all. Again, for all controls with the same binding source. All controls works in the synchronized way with such implementation.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜