重构Bloated ViewModel

时间:2010-03-25 13:45:09

标签: wpf mvvm

我正在编写PRISM / MVVM / WPF应用程序。它是一个LOB应用程序,因此存在许多复杂的规则。我注意到View Model开始变得臃肿。有两个主要问题。

一个是为了维护MVVM,我做了很多让我感觉很烦的事情,就像在我的VM中添加一堆属性一样。视图绑定到这些属性以跟踪视图特定信息的感觉。例如,布尔值跟踪VM中长时间运行的进程的状态,因此视图可以在长时间运行的进程工作时禁用其某些控件。我已经读过这个问题可以用Attached Behaviors解决。我会更多地了解这一点。在网上看到的示例MVVM应用程序中,这并不是什么大问题,因为它们过于简化了。

另一个问题是我的VM中的命令数量。现在有四个命令。我使用Josh Smith的RelayCommand(基本上是PRISM中的DelegateCommand)在VM中定义命令,因此所有业务逻辑都存在于VM中。我考虑将每个命令移动到单独的工作单元中。我不确定最好的方法。

您使用哪种模式来保持VM清洁?我已经可以感觉有人回答“你的观点和虚拟机太复杂,你应该将它们分解成许多视图/虚拟机”。从Ux的角度来看,它肯定不是太复杂 - 有2个按钮,一个组合框和一个列表框。此外,从逻辑的角度来看,它是一个有凝聚力的领域。话虽如此,我很想知道其他人是如何处理这类问题的。

感谢您的意见。

3 个答案:

答案 0 :(得分:4)

我感觉到你的痛苦。在处理MVVM应用程序时,我经常与这些问题搏斗。有一天,我会发布一份问题清单,以便从其他人那里获得您的投入。

我倾向于非常担心我的ViewModel基类中的“膨胀”,而不是在具体的ViewModel子类中。将2-3个ViewModel使用的依赖项抛出到基类中通常很诱人,但应该避免使用它。

虽然我不能假设知道你对膨胀的看法是什么,但我可以说我不认为在VM中处理“忙”属性或命令是不好的。您可能会考虑的一件事是ViewModel是否可以一次忙于执行多个操作。如果是这样,你可能想继续思考如何分解它。虽然我没有亲自在实践中看到它,但你可能有一个单一的,有凝聚力的视图和几个ViewModel绑定它。

如果命令很长,或者它们可以在不同的目标上执行,我认为使命令自执行单元是个好主意。但最好与这种方法保持一致,以避免混淆后来使用它的人。例如,如果您有一个大约10行代码的SaveCustomerCommand类,您可能不希望将RelayCommand用于其他所有代码。

如果对这类内容有严格的规则,那就太好了,但我认为框架和模式现在仍处于进化阶段。

答案 1 :(得分:0)

在不了解ViewModel的细节的情况下,很难说出膨胀的来源。它可能与非UI相关,例如查询模型的行太多。或者它可能是在UI中使用触发器或其他东西更好地实现的UI功能。

你能详细说明你的ViewModel吗?

编辑根据评论。

SaveCustomer和DeleteCustomer听起来像模型或服务方法,因此我将它们放在某种持久层中。

上传/下载客户视频:同样,这些不是特定于ViewModel的(您可能希望在另一个ViewModel中执行这些操作),因此我将它们放在Web服务中并让ViewModel命令调用它。

一般来说,将公共代码(多个ViewModel可能想要使用)放入服务然后将该服务放入您的IOC是值得的。这样,您的ViewModel最终成为从View到Model或Service的信息的轻量级“导演”。

答案 2 :(得分:0)

我对此有两个主要想法,但是YMMV。

首先,我将实现特定代码从VM移动到'service',这基本上只是一个完成工作的类。这可以是例如具有save,delete,update方法的实现的'customerService'类,并且RelayCommand实现将调用服务来执行动作,并且适当地更新任何视图属性。

一个例子是RelayCommand可以设置“ShowProgressBar”属性的值,该属性绑定到进度条的可见性。然后它将调用服务中的save函数,并在完成时(这应该是异步btw)它应该再次更新“ShowProgressBar”。通过这种方式,ViewModel独立于核心应用程序逻辑控制View的状态。

其次,我将查看您正在使用的基本视图模型,并尽量保持尽可能简单,只添加所有视图模型共有的组件。然后,您可以根据需要创建其他基本视图模型或接口。例如,您可以决定所有视图模型将公开ShowProgressBar属性,该属性可以绑定到页面上的进度条,或者您可以决定创建在需要进度条的页面中继承/实现的ProgressViewModelBase或IAllowProgress。 / p>

View本身应该尽可能少的代码,你应该瞄准0代码隐藏(虽然有些事情只能在代码隐藏中完成)。

我发现有害的一件事是复杂视图模型的实例化在性能方面可能很昂贵,尤其是在注册relaycommands和事件聚合器源/侦听器时,因此保持基本视图模型非常有用。如果您使用ViewModelCollections显示Model对象的列表以及在UI线程上创建ViewModel的位置(这可能是在View Creation上实例化ViewModel),这一点尤其重要。