为啥绑定更新没有实现 INotifyPropertyChanged?

Posted

技术标签:

【中文标题】为啥绑定更新没有实现 INotifyPropertyChanged?【英文标题】:Why does the binding update without implementing INotifyPropertyChanged?为什么绑定更新没有实现 INotifyPropertyChanged? 【发布时间】:2011-12-07 17:00:20 【问题描述】:

我创建了一个 ViewModel 并将其属性绑定到 UI 上的两个文本框。当我更改 first 的值并 将焦点移出文本框 时,另一个文本框的值会发生变化,但我没有实现 INotifyPropertyChanged。这是如何工作的?

以下是 XAML

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    <StackPanel>
        <TextBox Text="Binding Name" />
        <TextBox Text="Binding Name" />
    </StackPanel>
</Window>

下面是我的 ViewModel

class ViewModel

    public string Name  get; set; 

【问题讨论】:

【参考方案1】:

我测试过,你是对的。现在我在网上搜索,找到了this。

抱歉这么久才回复,实际上你遇到了另一个WPF的隐藏方面,那就是WPF的数据绑定引擎将数据绑定到PropertyDescriptor实例如果源对象是普通 CLR 对象并且不实现 INotifyPropertyChanged 接口,则为源属性。并且数据绑定引擎将尝试通过 PropertyDescriptor.AddValueChanged() 方法订阅属性更改事件。而当目标数据绑定元素改变属性值时,数据绑定引擎会调用 PropertyDescriptor.SetValue() 方法将改变后的值传回源属性,同时会引发 ValueChanged 事件通知其他订阅者(在这种情况下,其他订阅者将是 ListBox 中的 TextBlock。

如果您正在实施 INotifyPropertyChanged,您将完全负责在需要将数据绑定到 UI 的属性的每个设置器中实施更改通知。否则,更改将不会像您期望的那样同步。

希望这能把事情弄清楚一点。

所以基本上你可以这样做,只要它是一个普通的 CLR 对象。非常整洁但完全出乎意料 - 过去几年我做了一些 WPF 工作。你永远不会停止学习新事物,对吧?

正如 Hasan Khan 所建议的,这里是 another link,这是一篇关于这个主题的非常有趣的文章。

注意这仅在使用绑定时有效。如果您从代码更新值,则更改不会收到通知。 [...]

WPF 在绑定时使用重量更轻的 PropertyInfo 类。如果显式实现 INotifyPropertyChanged,则 WPF 需要做的就是调用 PropertyInfo.GetValue 方法来获取最新值。这比获取所有描述符要少得多。描述符最终花费了属性信息类内存的 4 倍。 [...]

实现 INotifyPropertyChanged 可能是相当乏味的开发工作。但是,您需要根据 WPF 应用程序的运行时占用空间(内存和 CPU)权衡该工作。 自己实现 INPC 将节省运行时 CPU 和内存

编辑:

更新这个,因为我仍然不时从这里获得 cmets 和 upvotes,所以它显然仍然相关,即使我自己已经有一段时间没有使用 WPF。但是,如 cmets 中所述,请注意这可能会导致 memory leaks。据说它对反射的使用也很重,这也被提到了。

【讨论】:

+1 我多年来一直在使用 WPF,但从未意识到这一点 - 也许您应该将它放在此处的“隐藏 WPF 功能”线程中? 有趣。我不知道我现在也在 WPF 中工作了很长一段时间。 @AngelWPF - Hasan Khan 发布的链接解释说,如果从 UI 更改属性,它只会导致 UI 更新 - 不会传播代码的更改 我已经用我认为有价值的所有相关信息更新了我的答案,以便快速浏览。又一天,又一个秘密暴露了!我喜欢 *** :) 所以,人们应该意识到,如果你不在 CLR 对象上实现 INotifyPropertyChanged ......你会导致内存泄漏。见cplotts.com/2009/04/14/wpf-memory-leaks。【参考方案2】:

我刚刚发现这也适用于 WinForms,有点:/

public class Test

    public bool IsEnabled  get; set; 


var test = new Test();
var btn = new Button  Enabled = false, Text = "Button" ;
var binding = new Binding("Enabled", test, "IsEnabled");
btn.DataBindings.Add(binding);
var frm = new Form();
frm.Controls.Add(btn);
test.IsEnabled = true;
Application.Run(frm);

奇怪的是,这并没有禁用按钮:

btn.Enabled = false;

这样做:

test.IsEnabled = false;

【讨论】:

【参考方案3】:

我可以解释为什么当焦点改变时属性会更新:所有Bindings 都有一个UpdateSourceTrigger 属性,它指示何时源属性将被更新。此默认值在每个DependencyProperty 上定义,TextBox.Text 属性设置为LostFocus,这意味着当控件失去焦点时将更新该属性。

我相信 UrbanEsc 的回答解释了为什么值会被更新

【讨论】:

以上是关于为啥绑定更新没有实现 INotifyPropertyChanged?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我工作时状态栏没有更新?

为啥双向绑定组件在 Svelte 中更新两次?

为啥我的 ko 计算 observable 在其值更改时不会更新绑定的 UI 元素?

xamarin 表单 - 与选择器的两种方式绑定 - 为啥我不能从后面的代码更新选择器?

在 Winform 中,为啥当我在一个数据源上调用 PropertyChanged 时所有绑定的属性都会更新?

为啥Unity没有实现iOS平台代码热更新