【问题标题】:How should the model be implemented in MVVM?模型应该如何在 MVVM 中实现?
【发布时间】:2012-08-16 22:41:30
【问题描述】:

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

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

ViewModel应该实现INotifyPropertyChanged接口,以便通知View发生了变化,但是Model如何与ViewModel通信呢?看了this article,才知道INotifyPropertyChanged接口是模型实现的。 This answer 解释了一点,但让我有点困惑:

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

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

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

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

【问题讨论】:

    标签: c# .net mvvm


    【解决方案1】:

    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。

    【讨论】:

      【解决方案2】:
      1. INotifyPropertyChanged 主要由 ViewModel 类实现。这是为了方便数据绑定,以便视图中绑定到 ViewModel 属性的 UI 控件会在属性被修改时更新。
        在 MVVM 设计模式中,关系非常简单并且是单一方向的。 View 知道它是 ViewModel,而 ViewModel 知道 Model。如果模型更新了,视图模型需要以某种方式知道,以便它可以反映更新并将其传播到视图。一种方法是让 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 的实现控制着什么会被写回你的模型。

      【讨论】:

      • 这篇文章显示绑定到字典:stackoverflow.com/questions/3334128/…
      • @Bob Horn - 是的,在这种情况下,字典的值可能会通过数据绑定被拉入控件,但是当字典中的值发生变化时它如何更新?鉴于没有提供通知,我不相信它确实如此。
      • 好点。无论字典的任何更新都必须调用 OnPropertyChanged,这并不理想。
      • 没有没有。 INotifyCollectionChanged 用于通知集合更改。 INotifyPropertyXXXXX 用于通知对象/属性更改,并且 INotifyCollectionChanged 通知对象/集合相同,并且仅更新了项目列表。可悲的是, Dictionary/IDictionary 缺少这一点。但是您可以解决这个问题:该文章中提供的绑定绑定到字典,但 不使用它 - 不使用 [key]。实际上,它绑定到IEnumerable<KeyValuePair<,>>,因此可以将其包装到可刷新的集合视图中,而不是暴露字典,等等。
      【解决方案3】:

      INotifyPropertyChanged (INPC) 应该永远在模型中,除非模型也是 ViewModel(即您没有“模型”)。 INPC 应该只在视图模型中。

      模型应该对视图模型一无所知,因此可以永远与它通信。只有视图模型可以与模型通信。

      从 UI 的角度来看,只有视图模型对数据做任何事情;所以,如果你希望模型是“只读的”,那么就不要在视图模型中实现它。

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

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

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

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

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

      这意味着 INPC 的任何特定实现都真正绑定到一个且只有一个 UI 框架。该对象不能再在 anywhere 中使用(显然 anywhere 是一个理想的选择,通常不可能使任何事情都适用于每个场景——这里的重点是超过只是一两个场景)。例如如果在多线程环境中使用 INPC 的实现,则它需要在发送属性通知之前将数据“编组”回 UI 线程。在 WinForms 中是 Control.BeginInvoke,在 WPF 和 Silverlight 中是通过 System.Windows.Threading.Dispatcher。在 WinRT 中,这是通过 Windows.UI.CoreDispatcher。在所有情况下,INPC 实现都必须直接耦合到一个 UI 框架。对于 Silverlight,这是与“桌面”Dispatcher 或 Windows Phone Dispatcher 的直接耦合。

      质量指标包括凝聚力等概念。内聚度是衡量两个代码单元相关程度的指标。由于支持特定 UI 框架所需的所有基础设施的性质,由 UI 以外的其他东西使用的 INPC 实现,虽然可能能够在 UI 之外使用,但内聚性较低,因为所有不会使用与 UI 框架相关的代码。即,与 UI 完全分离需要承担太多责任。是的,您可以在任何地方使用实现 INPC 的对象,并且从不使用 PropertyChanged 事件,但是您的凝聚力很低(被认为不好)。

      如果我在我的模型中实现了 INPC,并且我想将该模型与我的 UI 和我的 WCF 或 Web 服务后端一起使用,我要么不能,要么我的后端将不得不参考一些 UI 框架。如果我想在另一种类型的 UI 中使用该模型,我不能,因为 INPC 实现依赖于一个特定的 UI 框架。我不得不写另一个“模型”;在这一点上,它显然是一个“视图模型”。

      INPC 本身并不绑定到特定的 UI 框架(也不应该如此)。这导致了一种误解,即 INPC 可以在任何地方使用。 是的,它缺乏与高级命名空间的耦合意味着它可以,但是 INPC 的压倒性使用是当目标是 UI 时。我会挑战 INPC 的任何其他不涉及 UI 作为真正“绑定”的用途。与任何其他工具一样,您可以滥用它来获得有用的结果。 INPC用于投影数据,可用于转换数据等;但我相信这些是对 INPC 的滥用,并且确实超出了这个问题的重点......

      【讨论】:

      • 从不/永远?!甚至 EntityFramework 也使用它。您是说 EntityF 是 ViewModel 吗?您在 INPChanged 用法的开头所写的是完全错误的。 INPC比 XAML、WPF 或 MVVM 本身更老。太老了,那个好的 ol' WinForms 使用了它。这是一个通用接口,描述了通知属性更改的前提。你可以在任何你喜欢的地方使用它。 WPF 听到这一事实是一个巧合,或者更确切地说,是对 MS 架构师的程序员的一个很好的点头,因为他们使用了一个众所周知的接口。你只需要知道你在做什么。
      • 没错,Model 不应该知道 ViewModel,但是为什么这意味着它不能与之通信呢?演讲者不需要认识听众中的每个人就可以给他们演讲。
      • @quetzalcoatl EF 是一个数据模型。您可以灵活地使用 EDM 做什么,但它们仍然存在阻抗不匹配的问题。通过数据绑定直接在您的 UI 中使用 EF 实体将您的 UI 直接耦合到您的数据模型。这也是 EF 的 POCO 能力如此受欢迎的原因之一。 EF 也会出现同样的耦合问题,如果我在后台线程上更新数据,我的实体就不需要知道如何编组到 UI 线程——你不会使用 EF 开箱即用。 “WinForms”,是的,不是 UI 框架。
      • @PeterRitchie 我想我们对 MVVM 模式有不同的看法。 VM 负责表示逻辑和数据以支持视图。它不知道模型中的数据是如何来源的,也不知道模型中可能导致它发生变化的触发器。至于批量 UI 更新,如果模型通知它更改,VM 可以轻松处理。 VM 决定何时通知 View。模型可能不在乎。它只是在说“仅供参考,这里发生了一些变化”。
      • 但最终,在后期阶段,他们似乎都在分配职责和沟通方面存在问题。这是我们应该回顾核心的地方。由于 MVVM 旨在简化与代码分离的 UI 设计,它并不关心业务。它只关心视觉/视图和绑定方面。从它的角度来看,模型就是“其他一切”。因此,对我们来说,必须进一步分析模型。例如,尝试将其逻辑拆分为“控制器”、“业务逻辑”和“数据模型”。 A 到那个“ViewModel”和“View”。
      猜你喜欢
      • 2011-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-27
      • 2011-01-15
      • 2023-04-07
      • 1970-01-01
      相关资源
      最近更新 更多