如何在MVVM中实现模型?

时间:2012-08-16 22:41:30

标签: c# .net mvvm

我正在创建一个与一个或多个服务通信的简单网络应用程序,因此我计划使用一些队列(用于传出消息和传入消息),一个表,一个包含每个活动连接状态的列表,等等:换句话说,这些是确保应用程序本身运行所需的数据结构。

此应用程序还必须配备一个图形界面,显示应用程序本身的部分内部功能:例如,队列的填充状态,连接的状态(检测到的速度等)等。根据Model-View-ViewModel模式,模型由要在GUI中显示的数据组成:在该应用中,前述数据结构表示模型。换句话说, Model 实现了应用程序的业务逻辑。

ViewModel 应实现INotifyPropertyChanged接口以通知 View 发生了更改,但模型如何与ViewModel通信?阅读this article后,我意识到模型实现了INotifyPropertyChanged接口。 This answer解释了一点,但它让我有点困惑:

  

INotifyPropertyChanged - 应该进入ViewModel和Model(如果需要)

为什么,如果需要? 我什么时候应该实现这个界面? 什么时候不应该实施呢?

此外,Dictionary没有实现INotifyPropertyChanged接口:如果我使用它,我应该用实现此接口的类包装它吗?

最后,模型应该是只读的,这意味着用户无法使用GUI更改内部数据结构的内容。如何做到这一点?

3 个答案:

答案 0 :(得分:2)

how does the Model to communicate with the ViewModel

你想要的任何方式。在我们编写的大多数应用程序中,视图模型调用业务逻辑层(模型)。但是,如果您需要立即通知视图模型(事件)模型的更改,则可以在模型上实现INotifyPropertyChanged。或者您可以让视图模型订阅模型上的事件。

Moreover, the Dictionary does not implement the INotifyPropertyChanged interface: if I use it, should I wrap it with a class which implements this interface?

您只需要让视图模型实现INotifyPropertyChanged。视图模型(字典)中的属性将只调用NotifyPropertyChanged(或者您的实现的任何内容)。

Finally, the model should be read-only, meaning that the user does not be able to change the contents of internal data structures using the GUI. How to accomplish this?

不要为用户提供让他们更改数据的功能。以单向方式进行绑定,或者根本不为它们提供用于进行更改的API。

答案 1 :(得分:1)

  1. INotifyPropertyChanged主要由ViewModel类实现。这是为了便于数据绑定,以便在修改属性时更新视图中绑定到ViewModel属性的UI控件。
    在MVVM设计模式中,关系非常简单并且在单一方向上。 View知道它是ViewModel,而ViewModel知道Model。如果模型已更新,ViewModel需要以某种方式知道,以便它可以反映更新并将其传播到View。一种方法是使Model实现INotifyPropertyChanged并让ViewModel实现相应的事件处理程序。如果所有更改都是从UI驱动并被推回到模型,那么这可能不是必需的。

  2. 您无法真正绑定到词典。如果适用于您的情况,使用ObservableCollection将是理想的选择。或者,你可以看看如何实现一个Observable Dictionary:http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/

  3. MVVM提供了模型与视图的分离,因此视图与模型之间不应存在直接关系。 ViewModel的实现控制了什么,如果有什么东西可以写回你的模型。

答案 2 :(得分:0)

INotifyPropertyChanged(INPC)永远不会在模型中,除非模型也是ViewModel(即你没有"模型") 。 INPC应该只在视图模型中。

模型应该对视图模型一无所知,因此永远不会与之通信。只有视图模型才能与模型通信。

从UI的角度来看,只有视图模型对数据做任何事情;因此,如果您希望模型为"只读",那么就不要在视图模型中实现该模型。

绑定将使用视图模型完成,在这种情况下,不要使用Dictionary(除非你想编写代码来包装它以便绑定它)。如果字典在模型中,那么你应该"包装"在视图模型中 - 在集合周围编写一个可观察的包装器是相当简单的。很可能你的视图模型不会处理键/值对 - 它应该处理UI可以处理(和绑定)的平面。

<强>更新
INPC被引入用于数据绑定。它将视图与特定的具体类分离,因此它只需要知道INPC(注意解耦的方向)。就MVVM而言,这将视图与视图模型分离,在PM的情况下,这可以将视图与演示者分离,在MVC的情况下,这可以将视图与控制器分离,在MVP的情况下,这使视图分离。主持人的观点。

数据绑定是一种将数据绑定到UI元素的技术。它将数据源绑定到目标,以便目标可以请求数据它看起来是否合适,或者源可以推送数据,但它认为合适(取决于类型的绑定 - 它可以是单向的或静态的,限制了get / push的发生频率。)

有时,数据源与目标之间分离关系的必要性使人们相信数据绑定不是UI问题,数据绑定可以应用于任何地方。即,数据绑定实现与UI完全分离。这通常是一个错误。数据绑定将视图与特定类的特定知识分离(这是基本的分层和避免循环,我不会进入这里)。但是,它并没有完全将视图与数据源分离。绑定不会在没有数据源的情况下发生 - 那里仍然存在一定程度的耦合,它只是已经减轻的编译时耦合(有助于测试,灵活性,健壮性等等但必须在运行时出现在生产中。即INPC实现可以在运行时不受UI元素限制的事实并不意味着它不依赖于UI框架) 。视图仍与数据源松散耦合这一事实并不是这种关系中唯一的耦合。数据源通过其UI框架松散地(如果不是松散)将耦合到视图。

每个UI框架都有一个限制,即必须在主要或UI线程上完成对UI元素的访问和修改。 (至少在Windows上;它可能发生在其他平台上,我只是不精通其他任何平台)。使用数据绑定,源间接绑定到控件,任何数据更改都会直接更改一个或多个UI元素(取决于您可以拥有中介的框架。如WinRT中的值转换器,但他们的责任是转换或转换数据)。这意味着数据源需要具有 绑定到UI以及它绑定到的UI框架的类型的知识。这种与UI框架的紧密耦合显然将数据源(仍然松散地)耦合到UI。

这意味着INPC的任何特定实现实际上都绑定到一个且只有一个UI框架。该对象不能再用于任何地方(显然任何地方是理想的,它通常不可能使任何事情适用于每个场景 - 这里的要点很高凝聚力超过一两个场景)。例如如果在多线程环境中使用INPC的实现,那么它需要&#34; marshal&#34;在发送属性通知之前将数据返回到UI线程。在WinForms中,这是Control.BeginInvoke,在WPF和Silverlight中通过System.Windows.Threading.Dispatcher。在WinRT中,通过Windows.UI.CoreDispatcher。在所有情况下,INPC实现必须直接耦合到一个UI框架。在Silverlight的情况下,这是与桌面的直接耦合&#34; Dispatcher或Windows Phone Dispatcher

质量指标包括凝聚力等概念。内聚力衡量两个代码单元的相关程度。由于支持一个特定UI框架所需的所有基础结构的性质,虽然可能在UI之外使用,但由UI以外的其他东西使用的INPC的实现将具有低内聚性,因为所有的不会使用与UI框架相关的代码。也就是说它完全脱离了UI,承担了太多的责任。是的,你可以使用在任何地方实现INPC的对象,并且永远不会使用PropertyChanged事件,但是你的系数很低(被认为是坏的)。

如果我在我的模型中实现了INPC,并且我想在我的UI和我的WCF或Web服务后端使用该模型,我要么不能或我的后端必须引用一些UI框架。如果我想在另一种类型的UI中使用该模型,我无法做到,因为INPC实现依赖于一个特定的UI框架。我必须写另一个&#34;模型&#34 ;;在这一点上,它显然是一个&#34;视图模型&#34;。

INPC本身并不局限于特定的UI框架(也不应该)。这导致误解INPC可以在任何地方使用。 ,它缺乏与高级命名空间的耦合意味着可以,但INPC的压倒性使用是当目标是UI时。我挑战INPC的任何其他用途,不要将UI视为真实的&#34;绑定&#34;。与任何其他工具一样,您可以滥用它来获得有用的结果。 INPC 可以用于投影数据,它可以用于转换数据等;但我相信那些是INPC的误用,并且真的不在这个问题的焦点之内......