4

我有一个这样的实体:

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

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

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

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

当按钮点击时,我们直接通过如下代码更新源代码:

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

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

4

3 回答 3

4

原因是PropertyDescriptor,看下面的线程,同样的问题被问到:数据绑定系统如何知道属性何时发生变化?

这里有两个答案

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


我不在微软,但我可以确认。如果使用 PropertyDescriptor 来更新值,那么将自动传播相关的更改通知。

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

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

并将以下代码添加到MainWindowctor

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");
}

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

于 2011-08-12T08:44:06.993 回答
1

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

于 2011-08-12T08:48:45.930 回答
1

好吧,正如你所说,你只需要实施INotifyPropertyChanged

当您从代码PropertyChanged设置属性并且需要将此更改反映到您的 UI 时使用该事件(UI 将捕获该事件,并且感谢您的 UI 将被更新)。另一边(从 UI 更改)不需要任何,这就是你得到这种行为的原因PropertyChangedUpdateSourceTriggerPropertyChanged

试试这样:

    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");
      }
    }

}

使用此代码,当您设置名称时,PropertyChanged将触发事件并相应地更新 UI :)

于 2011-08-12T08:23:41.467 回答