ViewModel中的INotifyPropertyChanged与DependencyProperty

bit*_*onk 349 architecture data-binding wpf dependency-properties mvvm

在Model-View-ViewModel体系结构WPF应用程序中实现ViewModel时,似乎有两个主要选择如何使其成为databindable.我已经看到了DependencyProperty用于View将要绑定的属性的实现,我已经看到了实现的ViewModel INotifyPropertyChanged.

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

小智 212

Kent写了一篇关于这个主题的有趣博客:视图模型:POCO与DependencyObjects.

简短的摘要:

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

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

  • 很明显,Dependecy属性仅为UI而不是业务层构建. (26认同)
  • DependencyObject也依赖于WPF库,而POCO则没有,允许您的视图模型驱动WPF不可用的其他UI堆栈(Compact Framework,Mono). (23认同)
  • 依赖属性还需要DependencyObject父级.您的ViewModel确实不应该从DependencyObject继承. (10认同)

小智 37

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

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


Job*_*Joy 27

选择完全基于您的业务逻辑和UI抽象级别.如果你不想要一个好的分离,那么DP将为你工作.

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

请参阅此文章 - /sf/ask/19256891/.该链接有很多对Model-View-ViewModel模式的引用,这与本讨论非常相关.

  • jbe的帖子更准确地回答了这些差异.仅仅因为VM(或Presenter)继承自DependencyObject并不意味着它不能被设置样式或者在逻辑上不与View分离,它只是意味着属性值的存储不同于显式声明的字段. POCO风格.话虽这么说,序列化,逻辑相等和线程关联是基于DepedencyObject的VM必须处理的实际问题. (9认同)

Bry*_*tts 19

从表现力的角度来看,我非常喜欢使用依赖属性并且在思考时会感到畏缩INotifyPropertyChanged.除了string由于事件订阅而导致的属性名称和可能的内存泄漏之外,INotifyPropertyChanged还有一个更加明确的机制.

依赖属性意味着"当这样做时,使用容易理解的静态元数据".这是一种声明性的方法,让我对优雅的投票.


Ada*_*dam 16

INotifyPropertyChanged 使用时还可以在getter的代码和属性的setter中添加更多逻辑.

DependencyProperty 例:

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

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}
Run Code Online (Sandbox Code Playgroud)

在你的getter和setter ---所有你可以做的只是分别调用SetValue和GetValue,b/c在框架的其他部分没有调用getter/setter,而是直接调用SetValue,GetValue,所以你的属性逻辑不会可靠地执行.

INotifyPropertyChanged,定义一个事件:

public event PropertyChangedEventHandler PropertyChanged;
Run Code Online (Sandbox Code Playgroud)

然后在代码中的任何地方简单地使用任何逻辑,然后调用:

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

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

// More cool stuff that will reliably happen...
Run Code Online (Sandbox Code Playgroud)

这可能是在getter/setter或其他任何地方.

  • 您也可以从DependencyProperties获取更改通知.请参见PropertyMetadata.PropertyChangedCallback.示例:http://msdn.microsoft.com/en-us/library/ms745795.aspx (11认同)
  • 此外,您还可以从任何地方调用SetValue,而不仅仅是从属性内部调用 (2认同)

小智 16

依赖属性旨在支持UI元素上的绑定(作为目标)而不是数据绑定的源,这是INotifyProperty的用武之地.从纯粹的角度来看,不应该在ViewModel上使用DP.

"为了成为绑定的源,属性不需要是依赖属性;您可以使用任何CLR属性作为绑定源.但是,为了成为绑定的目标,属性必须是依赖属性.要使单向或双向绑定生效,源属性必须支持传播到绑定系统并因此传播到目标的更改通知.对于自定义CLR绑定源,这意味着该属性必须支持INotifyPropertyChanged.集合应该支持INotifyCollectionChanged."

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

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

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

http://msdn.microsoft.com/en-us/library/cc903933(VS.95).aspx


mor*_*lli 7

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

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

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

所以在这里我发现INotifyPropertyChanged更好地将属性从业务逻辑暴露到GUI.

这就是说,我需要一个自定义GUI小部件来公开一个属性,并且对该属性的更改影响其他GUI小部件,DP证明了这个简单的解决方案.

所以我发现DP对GUI到GUI通知很有用.


Bry*_*tts 6

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

.NET 4.0将具有System.Xaml.dll,因此您不必依赖任意框架来使用它.请参阅Rob Relyea关于他的PDC会议帖子.

我的看法

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

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

XAML的目的是声明性对象图.W*F技术是这种范例的理想选择,但XAML独立存在.

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


Joh*_*ers 6

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