为啥绑定更新没有实现 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】:我可以解释为什么当焦点改变时属性会更新:所有Binding
s 都有一个UpdateSourceTrigger
属性,它指示何时源属性将被更新。此默认值在每个DependencyProperty
上定义,TextBox.Text
属性设置为LostFocus
,这意味着当控件失去焦点时将更新该属性。
我相信 UrbanEsc 的回答解释了为什么值会被更新
【讨论】:
以上是关于为啥绑定更新没有实现 INotifyPropertyChanged?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 ko 计算 observable 在其值更改时不会更新绑定的 UI 元素?
xamarin 表单 - 与选择器的两种方式绑定 - 为啥我不能从后面的代码更新选择器?