INotifyPropertyChanged vs. ViewModel中的DependencyProperty

当在一个模型-视图-视图模型架构的WPF应用程序中实现ViewModel时,似乎有两个主要的选择:如何使它可数据化。我曾经见过将DependencyProperty用于视图将要绑定的属性的实现,我也见过ViewModel实现了INotifyPropertyChanged

我的问题是,什么时候我应该更喜欢其中一个?有什么性能差异吗?把ViewModel依赖项交给WPF真的是个好主意吗?在做设计决定时,我还需要考虑什么?

78576 次浏览

选择完全取决于您的业务逻辑和UI抽象级别。如果你不想好好分开,那么DP可以帮你。

DependencyProperties将主要适用于VisualElements级别,所以如果我们为每个业务需求创建大量的dp并不是一个好主意。DP的开销也比INotifyPropertyChanged大。当你设计WPF/Silverlight时,试着把UI和ViewModel完全分开,这样我们就可以随时改变布局和UI控件(基于主题和样式)。

也可以参考这篇文章——https://stackoverflow.com/questions/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel。该链接有很多参考模型-视图-视图模型模式,这与本文的讨论非常相关。

把ViewModel依赖项交给WPF真的是个好主意吗?

. net 4.0将有System.Xaml.dll,所以你不必依赖于任意的框架来利用它。参见Rob信的关于他的PDC会话的帖子。

我的看法

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

它们的关系类似于c#(一种描述逻辑的语言)和。net(一种实现特定类型逻辑的框架)。

XAML的目的是声明性的对象图。W*F技术是这种范例的很好的候选者,但是XAML独立于它们而存在。

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

从表达的角度来看,我非常喜欢使用依赖属性,一想到INotifyPropertyChanged就畏缩。除了string属性名和由于事件订阅可能导致的内存泄漏外,INotifyPropertyChanged是一个更显式的机制。

依赖属性意味着使用易于理解的静态元数据“当这个时,做那个”。这是一种声明性的方法,我认为它很优雅。

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

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

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

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

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

所以我发现DP对于GUI到GUI的通知很有用。

我更喜欢更直接的方法,我在没有INotifyPropertyChanged的表示模型中写过。使用数据绑定的替代方案,您可以直接绑定到CLR属性,而不需要任何簿记代码。您只需在视图模型中编写普通的。net代码,当数据模型发生变化时,它就会更新。

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

在你的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中,也可以在其他任何地方。

看起来依赖属性应该用在你创建的控件中,比如按钮。要在XAML中使用属性并使用所有WPF特性,这些属性必须具有依赖属性。

然而,你的ViewModel最好使用INotifyPropertyChanged。使用INotifyPropertyChanged可以让你在需要的时候拥有getter/setter逻辑。

我建议查看Josh Smith的基类版本,它已经实现了INotifyPropertyChanged:

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

我认为这是如何创建ViewModel的一个很好的例子。

Kent写了一篇关于这个话题的有趣博客:视图模型:POCOs vs依赖对象

简短的总结:

  1. DependencyObjects不被标记为 可序列化的李< / >
  2. DependencyObject类覆盖并密封Equals()和 李GetHashCode方法()方法< / >
  3. DependencyObject具有线程亲和性-它只能被访问 就在那根线上 李创建< / >

我更喜欢POCO方法。PresentationModel(又名ViewModel)的基类实现了INotifyPropertyChanged接口,可以在这里找到:http://compositeextensions.codeplex.com

我认为DependencyProperty和INotifyPropertyChanged在绑定中用于两种不同的事情:第一个用于使一个属性成为绑定的目标并接收来自另一个属性的输入(使用{Binding…}来设置属性),当你想要一个属性的值被用作绑定的源(绑定路径表达式中的名称)时,最后一个。

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

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

如果你想将属性暴露给其他控件,你必须使用Dependency属性…但祝你好运,因为他们需要一段时间来弄清楚…

只有一件事为什么更喜欢DependencyObject - Binding会工作得更好。只需要尝试一个带有ListBoxTextBox的例子,用来自INotifyPropertyChanged属性vs. DependencyProperty的数据填充列表,并从TextBox编辑当前项…

依赖属性旨在支持在UI元素上绑定(作为目标),而不是作为数据绑定的源,这就是INotifyProperty的作用。从纯粹的角度来看,你不应该在ViewModels上使用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

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