ViewModel中的INotifyPropertyChanged与DependencyProperty


353

在Model-View-ViewModel体系结构WPF应用程序中实现ViewModel时,似乎有两个主要选择来使它可数据绑定。我已经看到DependencyProperty了将View绑定到的属性的实现,并且看到了ViewModel的实现INotifyPropertyChanged

我的问题是,什么时候我应该优先选择另一个?有性能差异吗?将ViewModel依赖项提供给WPF真的是一个好主意吗?做出设计决策时,我还需要考虑什么?


11
有关实现INotifyPropertyChanged的编译器检查方式,请参见stackoverflow.com/questions/1329138/…。避免将属性名称作为魔术字符串使用。
伊恩·林格罗斯(Ean Ringrose)2009年

10
通常,在实现INotifyPropertyChanged的类中,依赖项属性和常规属性之间存在主要区别。依赖关系属性可以是数据绑定中的源或目标,但是具有INotifyPropertyChanged支持的常规属性只能用作源。因此,这些解决方案不能完全互换。数据绑定基础结构需要以DP为目标才能工作,但是源可以是具有INotifyPropertyChanged支持的常规属性,也可以是通用DP。
Mostafa Rezaei

4
有关.net 4.5的实现方式,请参见stackoverflow.com/a/10595688/200442INotifyPropertyChanged
丹尼尔·

Answers:


214

肯特(Kent)撰写了有关该主题的有趣博客:“ 视图模型:POCO与DependencyObjects”

简短的摘要:

  1. DependencyObjects未标记为可序列化
  2. DependencyObject类重写并密封Equals()和GetHashCode()方法
  3. DependencyObject具有线程关联性-只能在创建它的线程上访问

我更喜欢POCO方法。可以在以下位置找到实现INotifyPropertyChanged接口的PresentationModel(也称为ViewModel)的基类:http : //compositeextensions.codeplex.com


24
DependencyObject还依赖WPF库,而POCO不依赖于WPF库,从而允许您的视图模型驱动WPF不可用的其他UI堆栈(Compact Framework,Mono)。
codekaizen

26
显然,Dependecy属性仅是为UI而没有为业务层构建的。
安德烈·里内(AndreiRînea),2010年

11
依赖属性还需要DependencyObject父对象。您的ViewModel实际上不应该从DependencyObject继承。
Gusdor 2011年

38

根据WPF性能指南,DependencyObjects的性能肯定比实现INotifyPropertyChanged的POCO更好:

http://msdn.microsoft.com/en-us/library/bb613546.aspx



如果选择.NET Framework版本4,则该链接仍然有效。不适用于“当前版本”。
doubleYou

感谢您指出这一点,在开发人员中有很多丑闻错误的信息,他们愚蠢地宣称INotifyPropertyChanged比DP更快或更省钱,这完全是没有根据的。DP是在结构上定义虚拟(数据)树的快速,优雅且强大的方法。
tpartee '17

DependencyObjects有一个隐藏的弊端。它们需要在与绑定到它们的控件相同的线程上创建。这意味着GUI线程。这意味着您需要将创建的内容分发到该线程。例如,您不能在DB的某些后台线程上加载和创建这些东西。除非您分派创作。疯。
ed版

28

该选择完全基于您的业务逻辑和UI抽象级别。如果您不希望分开,那么DP将为您工作。

DependencyProperties主要适用于VisualElements级别,因此如果我们为我们的每个业务需求创建大量的DP,将不是一个好主意。而且,DP的成本比INotifyPropertyChanged高。当您设计WPF / Silverlight时,请尝试将UI和ViewModel设计为完全分开,以便在任何时候我们都可以更改Layout和UI控件(基于主题和样式)

另请参阅此帖子-https: //stackoverflow.com/questions/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel。该链接对Model-View-ViewModel模式有很多参考,与该讨论非常相关。


9
jbe的帖子更准确地回答了差异。仅仅因为VM(或Presenter)继承自DependencyObject并不意味着它不能被设置样式或在逻辑上与View分离,仅意味着属性值的存储与在View中显式声明的字段不同。 POCO风格。话虽如此,序列化,逻辑相等和线程亲和力是基于DepedencyObject的VM必须处理的实际问题。
米哈丹2009年

“ DP的成本也比INotifyPropertyChanged高”-您在这方面的证据来源在哪里?许多开发人员在没有任何证据的情况下提出此主张。根据MSDN,这不是真的。“尝试将UI和ViewModel的设计完全分开,以便我们可以随时更改Layout和UI控件” –再次,这与POCO + PropChange与DO / DP绝对无关。如果有的话,DO / DP中的反射和路径注册表可提高您在视觉方面的工作能力。
tpartee '17

20

从表达的角度来看,我非常喜欢使用依赖属性,并且对的想法有所畏惧INotifyPropertyChanged。除了string属性名称和事件订阅可能导致的内存泄漏之外,这INotifyPropertyChanged是一种更为明确的机制。

依赖项属性使用易于理解的静态元数据“在需要时执行此操作”。这是一种声明式方法,赢得了我的赞成。


1
字符串部分现在具有名称为operator的解决方案。
Newtopian

@Newtopian:是的。可能还有一些有趣的事情[CallerMemberName]
布莱恩·瓦茨

更不用说使用DO / DP模型而不是POCO时,WPF和CLR具有大量的财产注册(反射)优势。
tpartee '17

16

INotifyPropertyChanged 当使用时,还使您能够在获取器和属性的设置器的代码中添加更多逻辑。

DependencyProperty 例:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

在您的getter和setter中,您所能做的就是分别简单地分别调用SetValue和GetValue,在框架的其他部分中b / c不会调用getter / setter,而是直接调用SetValue,GetValue,因此您的属性逻辑不会可靠地执行。

使用INotifyPropertyChanged定义事件:

public event PropertyChangedEventHandler PropertyChanged;

然后只需在代码中的任何地方添加任何逻辑,然后调用:

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

这可以在getter / setter或其他任何地方。


11
您也可以从DependencyProperties获取更改通知。请参见PropertyMetadata.PropertyChangedCallback。示例位于:msdn.microsoft.com/en-us/library/ms745795.aspx
乔·怀特

2
另外,您也可以从任何地方调用SetValue,而不仅是从属性内部调用
aL3891 2011年

这具有误导性和不真实性-即使有“内部”更改,也有多种方法可以挂接到DP上的更改事件。其中一人被指出上述由乔·怀特
tpartee

16

依赖项属性旨在支持在UI元素上进行绑定(作为目标),而不是作为数据绑定的源,这就是INotifyProperty的用处。从纯粹的角度来看,您不应在ViewModels上使用DP。

“要成为绑定的源,属性不必是依赖项属性;可以将任何CLR属性用作绑定源。但是,要成为绑定的目标,该属性必须是依赖属性:为了使单向或双向绑定有效,源属性必须支持传播到绑定系统并进而传播到目标的更改通知;对于自定义CLR绑定源,这意味着该属性必须支持INotifyPropertyChanged。集合应支持INotifyCollectionChanged。”

所有依赖项对象无法序列化(这可能会妨碍ViewModels和DTO(POCO)的使用)。

与WPF相比,Silverlight中的DP之间存在差异。

http://msdn.microsoft.com/zh-CN/library/cc221408(v=VS.95).aspx

http://msdn.microsoft.com/zh-CN/library/cc903933(VS.95).aspx


自2009年以来,我一直在使用序列化的依赖对象,没有问题,所以不确定当您说“所有依赖对象无法序列化”时,您在说什么-是的。其实有很多选择:codeproject.com/Articles/61440/... emphess.net/2008/11/25/dependencyproperty-serialization 和我个人的最爱之一:只要提供后备存储的所有你的DPS,让那些串行化(在Google上进行2分钟的搜索后,尚无很好的简单示例可供使用,但我向您保证这是可行的)。
tpartee'7

7

我最近也不得不考虑这个决定。

我发现INotifyPropertyChanged机制更适合我的需求,因为它使我可以将GUI粘贴到现有的业务逻辑框架上,而不会重复状态。我使用的框架具有自己的观察者模式,可以很容易地将一个通知级别转发到下一个通知级别。我只是有一个从我的业务逻辑框架和INotifyPropertyChanged接口实现观察者接口的类。

使用DP,您无法定义自己存储状态的后端。我不得不让.net缓存我绑定到的每个状态项的副本。这似乎是不必要的开销-我的状态很大而且很复杂。

因此,在这里我发现INotifyPropertyChanged可以更好地将属性从业务逻辑公开到GUI。

话虽这么说,我需要一个定制的GUI小部件来公开一个属性,并且对该属性的更改会影响其他GUI小部件DP,这证明了简单的解决方案。

因此,我发现DP对于GUI到GUI的通知很有用。


6

将ViewModel依赖项提供给WPF真的是一个好主意吗?

.NET 4.0将具有System.Xaml.dll,因此您不必依赖于任意框架即可使用它。请参阅Rob Relyea关于其PDC会话帖子。

我拿

XAML是一种用于描述对象的语言,而WPF是一种框架,其描述的对象是UI元素。

它们的关系类似于描述逻辑的语言C#和实现特定逻辑的框架.NET。

XAML的目的是声明性对象图。W * F技术是该范例的理想候选者,但是XAML独立存在。

XAML和整个依赖项系统被实现为WF和WPF的单独堆栈,可能利用了不同团队的经验,而没有在它们之间创建依赖项(无双关语)。


通过回答,您似乎假设bitbonk认为XAML和WPF相同。ViewModels应该具有尽可能少的WPF相关性,而不是增加逻辑间隔,而是降低代码复杂性,并避免与在用户控件的代码中简单地编写逻辑有关的所有问题。您将不可避免地实现ICommand之类的WPF概念,并呈现只有WPF / Silverlight才能轻松包装的行为-视图模型中唯一的表示线程问题应为CollectionViews和ObservableCollection。
Gusdor 2011年

6

依赖项属性是自定义控件创建的粘合剂。如果您有兴趣在XAML设计时使用Intelli-sense在属性窗口中显示属性,则必须使用Dependency属性。在设计时,INPC永远不会在属性窗口中显示属性。


4

似乎应该在您创建的控件(如按钮)中使用依赖项属性。若要在XAML中使用属性并使用所有WPF功能,这些属性必须是“依赖项属性”。

但是,使用INotifyPropertyChanged最好使用ViewModel。如果需要,使用INotifyPropertyChanged将使您能够具有getter / setter逻辑。

我建议为已经实现INotifyPropertyChanged的ViewModel检出Josh Smith的基类版本:

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

我认为这是如何执行ViewModel的绝佳示例。


4

我认为DependencyProperty和INotifyPropertyChanged用于Binding中的两个不同事物:第一个用于使属性成为绑定的目标,并从另一个属性接收输入(使用{Binding ...}设置属性),最后一个当您希望将属性的值用作绑定源(绑定路径表达式中的名称)时。因此,选择仅仅是技术上的。


2
在两种情况下都可以使用INotifyPropertyChanged。您可以将TwoWay绑定到它。仅出于技术原因才需要DependencyProperty,因为仅对View对象执行某些操作(例如,在XAML中实例化View对象时设置一些属性)。ViewModel永远不需要DependencyProperty。
oillio 2011年

3

我更喜欢一种更直接的方法,该方法是我在Presentation Model中没有INotifyPropertyChanged的博客中发布的。使用数据绑定的替代方法,您可以直接绑定到CLR属性,而无需任何簿记代码。您只需在视图模型中编写普通的.NET代码,然后在您的数据模型更改时对其进行更新。


如果没有INotifyPropertyChangedPropertyDescriptor被使用,这会导致内存泄漏
提拉克

我在该博客文章中介绍的Update Controls库使用弱引用,而不是属性描述符。它不会泄漏内存。
Michael L Perry

1
迈克尔,您的库会生成很多代码。我看不到好处。我可以通过使用生成的PropertyChanged事件调用生成模型包装器来实现相同的目的。
Der_Meister '16

3

为什么偏爱DependencyObject绑定只有一件事-绑定会更好。只需尝试使用ListBox和的示例,TextBoxINotifyPropertyChanged属性vs.的数据填充列表,然后从中DependencyProperty编辑当前项目即可TextBox


1
请提供代码示例
Hassan Tareq

1

如果要将属性公开给其他控件,则必须使用Dependency属性。但是,祝您好运,因为它们需要一段时间才能解决...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.