WPF中PropertyChanged UpdataSourceTrigger的奇怪行为

时间:2022-01-21 17:26:08

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:

然后我在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.

奇怪的是,实体“Person”没有实现INotifyPropertyChanged,但是当一个文本框被更改时,它会修改源(Person对象),但我们没有引发属性更改事件,但其余两个文本框自动更改。

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.

它没有更新。所以我认为如果Person类实现了INotifyPropertyChanged,这种行为是正常的,但现在该类没有实现接口,但它也更新了接口。如果你有一些线索,请告诉我原因。谢谢。

3 个解决方案

#1


4  

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?

原因是PropertyDescriptor,请参阅以下主题,同样的问题是:数据绑定系统如何知道属性何时发生变化?

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).

我认为神奇之处在于绑定系统对PropertyDescriptor的使用(SetValue可能会引发一个ValueChanged - PropertyDescriptor可能是共享的,而事件是基于每个对象引发的)。


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.

我不是微软,但我可以证实。如果PropertyDescriptor用于更新值,则会自动传播相关的更改通知。

Edit
You can verify this by naming the Person DataContext object

编辑您可以通过命名Person DataContext对象来验证这一点

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

and add the following code to the MainWindow ctor

并将以下代码添加到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.

一旦更改了三个TextBox中任何一个的值,您将最终进入事件处理程序OnPropertyChanged。

#2


1  

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

好吧,正如你所说,你只需要实现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

当您从代码设置属性并且需要将此更改反映到您的UI时,将使用PropertyChanged事件(UI将导致PropertyChanged事件,并且由于您的UpdateSourceTrigger,UI将被更新)。另一方(从UI更改)不需要任何PropertyChanged,这就是你得到这种行为的原因

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 :)

使用此代码,当您设置Name时,将触发PropertyChanged事件,因此更新UI :)

#3


1  

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.

它不仅适用于updatesourcetrigger = propertychanged,还适用于默认(丢失焦点)值。除了@Meleak所说的,我想指出这是好行为。 ui所做的任何更改都会传播到所有绑定目标。绑定引擎希望立即将此更改传播到所有控件。如果您通过代码进行更改,而不是实现INotifyPropertyChanged - 则根本不会反映从代码中进行的更改。同样,对于具有相同绑定源的所有控件。所有控件都以这种实现的同步方式工作。

#1


4  

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?

原因是PropertyDescriptor,请参阅以下主题,同样的问题是:数据绑定系统如何知道属性何时发生变化?

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).

我认为神奇之处在于绑定系统对PropertyDescriptor的使用(SetValue可能会引发一个ValueChanged - PropertyDescriptor可能是共享的,而事件是基于每个对象引发的)。


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.

我不是微软,但我可以证实。如果PropertyDescriptor用于更新值,则会自动传播相关的更改通知。

Edit
You can verify this by naming the Person DataContext object

编辑您可以通过命名Person DataContext对象来验证这一点

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

and add the following code to the MainWindow ctor

并将以下代码添加到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.

一旦更改了三个TextBox中任何一个的值,您将最终进入事件处理程序OnPropertyChanged。

#2


1  

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

好吧,正如你所说,你只需要实现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

当您从代码设置属性并且需要将此更改反映到您的UI时,将使用PropertyChanged事件(UI将导致PropertyChanged事件,并且由于您的UpdateSourceTrigger,UI将被更新)。另一方(从UI更改)不需要任何PropertyChanged,这就是你得到这种行为的原因

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 :)

使用此代码,当您设置Name时,将触发PropertyChanged事件,因此更新UI :)

#3


1  

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.

它不仅适用于updatesourcetrigger = propertychanged,还适用于默认(丢失焦点)值。除了@Meleak所说的,我想指出这是好行为。 ui所做的任何更改都会传播到所有绑定目标。绑定引擎希望立即将此更改传播到所有控件。如果您通过代码进行更改,而不是实现INotifyPropertyChanged - 则根本不会反映从代码中进行的更改。同样,对于具有相同绑定源的所有控件。所有控件都以这种实现的同步方式工作。