为什么绑定更新而不实现 INotifyPropertyChanged?

发布于 2024-12-09 20:10:53 字数 855 浏览 0 评论 0原文

我创建了一个 ViewModel 并将其属性绑定到 UI 上的两个文本框。当我更改第一个文本框的值并将焦点移出文本框时,另一个文本框的值会发生变化,但我没有实现 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; }
}

I created a ViewModel and bound its property to two textboxes on UI. The value of the other textbox changes when I change the value of first and focus out of the textbox but I'm not implementing INotifyPropertyChanged. How is this working?

Following is 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>

And following is my ViewModel

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

十六岁半 2024-12-16 20:10:53

我测试了一下,你是对的。现在我在网上搜索它,发现 这个

很抱歉花了这么长时间才回复,实际上您遇到了 WPF 的另一个隐藏方面,那就是 WPF 的数据绑定引擎将数据绑定到包装的 PropertyDescriptor 实例源属性(如果源对象是普通 CLR 对象并且未实现 INotifyPropertyChanged 接口)。并且数据绑定引擎将尝试通过PropertyDescriptor.AddValueChanged()方法订阅属性更改事件。当目标数据绑定元素更改属性值时,数据绑定引擎将调用 PropertyDescriptor.SetValue() 方法将更改后的值传回源属性,并同时引发 ValueChanged 事件以通知其他订阅者(在本例中,其他订阅者将是列表框中的 TextBlock。

如果您正在实现 INotifyPropertyChanged,您将完全负责在需要将数据绑定到 UI 的属性的每个 setter 中实现更改通知。否则,更改将不会按照您的预期同步。

希望这能让事情变得更清楚。

基本上你可以做到这一点,只要它是一个简单的 CLR 对象,但完全出乎意料 - 我已经做了一些 WPF 工作。过去的几年里,你永远不会停止学习新事物,对吗?

正如哈桑·汗所建议的,这是另一个链接关于这个主题的一篇非常有趣的文章。

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

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

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

编辑:

更新此内容,因为我仍然时不时地从这里收到评论和点赞,所以它显然仍然相关,即使我自己已经有一段时间没有使用 WPF 了。但是,正如评论中提到的,请注意这可能会导致 内存泄漏。据说它也大量使用反射,这一点也已经提到过。

I tested it, you are right. Now i searched for it on the web, and found this.

Sorry to take so long to reply, actually you are encountering a another hidden aspect of WPF, that's it WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.

And if you are implementing INotifyPropertyChanged, you are fully responsible to implement the change notification in every setter of the properties which needs to be data bound to the UI. Otherwise, the change will be not synchronized as you'd expect.

Hope this clears things up a little bit.

So basically you can do this, as long as its a plain CLR object. Pretty neat but totally unexpected - and i have done a bit of WPF work the past years. You never stop learning new things, right?

As suggested by Hasan Khan, here is another link to a pretty interesting article on this subject.

Note this only works when using binding. If you update the values from code, the change won't be notified. [...]

WPF uses the much lighter weight PropertyInfo class when binding. If you explicitly implement INotifyPropertyChanged, all WPF needs to do is call the PropertyInfo.GetValue method to get the latest value. That's quite a bit less work than getting all the descriptors. Descriptors end up costing in the order of 4x the memory of the property info classes. [...]

Implementing INotifyPropertyChanged can be a fair bit of tedious development work. However, you'll need to weigh that work against the runtime footprint (memory and CPU) of your WPF application. Implementing INPC yourself will save runtime CPU and memory.

Edit:

Updating this, since i still get comments and upvotes now and then from here, so it clearly is still relevant, even thouh i myself have not worked with WPF for quite some time now. However, as mentioned in the comments, be aware that this may cause memory leaks. Its also supposedly heavy on the Reflection usage, which has been mentioned as well.

维持三分热 2024-12-16 20:10:53

我刚刚发现这也适用于 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;

I just found out that this also works in WinForms, kinda :/

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

Strangely though, this doesn't disable the button:

btn.Enabled = false;

This does:

test.IsEnabled = false;
皓月长歌 2024-12-16 20:10:53

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

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

I can explain why the property is updated when focus changes: all Bindings have an UpdateSourceTrigger property which indicates when the source property will be updated. The default value for this is defined on each DependencyProperty and for the TextBox.Text property is set to LostFocus, meaning that the property will be updated when the control loses focus.

I believe UrbanEsc's answer explains why the value is updated at all

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文