MVVM ViewModel应该执行types转换/validation吗?
我们刚刚进入WPF中的MVVM。
我们用视图中绑定的“强types”属性(int,double?等)实现了ViewModel。
types转换工作正常,大多数,所以input数据很简单。 但是我们遇到了validation问题。
例如,如果在绑定到数字属性的文本框中input非数字值,则转换将失败,该属性将永远不会设置,并且我们从来没有机会向用户提供正确的反馈。 更糟糕的是,该属性保留了当前的值,导致视图中显示的内容与ViewModel中的实际内容不匹配。
所有这些都可以用价值转换器来处理,我知道。 但是我看到了一些意见,认为转换不应成为观点的责任。 在视图中input的是string,转换,validation等应该是ViewModel的责任(所以这个说法是这样的)。
如果是这样的话,我们应该将ViewModel的大部分属性重写为string,并通过IErrorInfo接口提供错误信息。 它肯定会使更简单,更精简的XAML。 另一方面,从视图devise者的angular度来看,转换,validation等将更less声明性,明确和灵活。
在我们看来,这两种方法是根本不同的,所以在我们决定之前,我们希望对这个问题有所了解。
所以:ViewModels应该向视图公开一个简化的,基于文本的界面,并在内部处理转换? 或者应该ViewModel属性公开实际的数据types,把这些杂事留给视图来处理?
更新:
很难在这里挑选一个胜利者,但是我最终落在了一个或多或less像我自己一样的人身上。
具体来说,我们决定保持ViewModel属性的types。 主要原因是它在视图devise中的灵活性,以及XAML中显式声明转换/格式化的能力。
我注意到一个假设,你会不同意我们这个观点的devise是固定和准备好的。 因此,在视图中不需要做出关于转换,格式化等的决定。 但是我们的过程是一个敏捷的过程,我们并没有事先弄清UI的细节。
事实上,把UI的细节留下来留给创造力,而且在我看来,即使是经过精心devise的devise,在整个实施过程中也总是会变形的。
所有这一切的重点是,虽然商业规则执法当然属于ViewModel,但在我们看来,简单的转换和格式化是一个视图。 这可能听起来像异端,但我并不认为视图中的types转换需要unit testing(只要我们unit testing实际的types转换器)。
总而言之,一个伟大的讨论,伙计,制定完善,知情的意见。 谢谢。
这是一个非常有趣的问题,我不觉得有一个明确的答案,但我会尽我所能把我的想法扔在那里。
根据我的理解,查看MVVM模式,ViewModel的要点是以一种View可以理解的方式公开数据, 而不需要对视图的使用方式进行任何假设。 举个例子,让我们假装我们正在模拟汽车的速度:
public class CarModel { public int MilesPerHour { get; set; } } public class CarViewModel { private CarModel _model; public int MilesPerHour { get { return _model.MilesPerHour; } set { _model.MilesPerHour = value; } } }
在上面的例子中,我将这个属性暴露为int,因为它就是模型中的内容。 您在问题中已经列出了这个缺点,但主要的优点是它为视图的创build者提供了有关如何显示该数据的有价值的信息。 请记住,我们(作为ViewModel的作者)不知道View的外观。 通过承诺数据是一个int的想法,View可以使用一个文本框或其他一些只接受数字的控件(例如一个拨号盘)来显示信息。 如果我们说我们要以一种我们认为对“视图”有帮助的方式来格式化数据,那么“视图”就会将这种重要的力量从它身上带走。
另一方面,我们在现实世界中工作。 我们倾向于知道什么是观点。 我们很less在同一个ViewModel之上插入和播放不同的视图,并将转换代码添加到ViewModel中更简单。 我不认为这是正确的,但这并不意味着你不会find我的生产代码使用它…
最后(我相信你知道这一点,但为了完成…)业务逻辑应该在ViewModel中完成。 如果我们认为汽车不应该超过70英里,那么执行这个任务不是视图的责任。 所以你仍然会得到某种错误提供者,但是在一个商业而不是显示水平。
好的,也许这不是最后的….
我想解决肯特提出的意见,我的想法不适合发表评论。
显然,我和Kent的观点(据我所知)之间的主要区别是他读取ViewModel是一个视图的模型,我读它是揭示模型的视图。 我承认有一点微妙的差别,但我认为结果是我不想删除模型提供的信息,即使它使得我使用的特定视图更容易。
我的观点是基于假设你应该能够交换意见,他们应该是短暂的事情,可能会根据屏幕大小,硬件,平台,延迟和环境的要求而改变。 有趣的是,我从来没有真正需要这个function,也没有看到曾经使用它的任何东西(除了概念应用的certificate),但是如果我们接受现在或将来不会使用它,每个ViewModel都可以和一个View一起使用,然后我们不妨将所有的代码都放到代码隐藏文件中,然后把ViewModel完全抛出去 – 毕竟它是如此的紧密结合,成为同一class
理想情况下,我希望ViewModel可以说“这个值是一个int,它总是一个int,你可以随意显示它,但是你可以给我任何东西回来,我会做我的最好使它适合,如果我不能,我会让你知道“。 基本上我的MilesPerHour属性应该有一个int getter,但是是一个对象设置器。 这样一来,这些视图就可以保存我所需要的所有信息,但不必担心转换或validation。
绝对的,它属于视图模型,所有通常的原因,包括:
- devise师拥有XAML。 您是否希望devise人员必须了解并实施必要的types转换和validation逻辑?
- 可测性。 你不想validation你的转换和validation逻辑工作正常吗? 如果它被embedded到视图中则更加困难。
另一方面,从视图devise者的angular度来看,转换,validation等将更less声明性,明确和灵活
我认为这是一个有争议的问题,因为视图devise师应该对这些事情负责。 devise师正在努力使UI看起来和感觉有一定的方式; 它是实现业务逻辑的开发人员,包括转换和validation逻辑。
这是一个很好的问题,我当然可以看到双方的讨论。
我的想法是,你真正想要的是一个适当的NumericInputControl
,你可以在你的xaml中使用。 这将提供更好的用户体验,因为您的用户将无法在数字字段中意外input文本,并且因为控件限制input而不validation它,所以您可以维护更强types的ViewModel。
我不知道如何去实现它,我知道经典的微调控制器/ NumericUpDown控件已经不受欢迎,因为它们不友好,但是我不相信这样的引入一个控件会违反devise方法或ViewModel的纯度。 您将收到一个数字,然后您可以在适当的位置进行范围validation,像往常一样通过IDataErrorInfo
提供反馈,等等。 :)这种技术可以让你在没有任何实际缺陷的情况下获得两全其美的好处(除了创build一个数字控件)。
MVVM ViewModel应该执行types转换/validation吗?
是的 。
视图模型是视图和模型之间的抽象层 – 执行任何types转换(而不是繁琐的值转换器)的理想场所。 validation应该绝对作为视图模型的一部分。
我们使用我们的视图模型尽可能处理数据types的转换。 这可以减less对某些非常特殊情况下的值转换器的需求。 你想公开什么types是最简单的视图消费。 这一直运作良好。
你提出的一个具体问题是:
例如,如果在绑定到数字属性的文本框中input非数字值,则转换将失败,该属性将永远不会设置,并且我们从来没有机会向用户提供正确的反馈。 更糟糕的是,该属性保留了它的当前值,导致视图中显示的内容与ViewModel中的实际内容不匹配。
可以通过将您的视图模型types暴露为可空types来处理。 即使input了无效的数据,这也应该允许更新底层的源代码,并触发validation。 这与我们使用DateTime和date时间select器的情况类似。
保持视图愚蠢。 我们没有官方devise师,我们的开发人员是我们的devise师,所以保持观点愚蠢有一些好处:
- 我们(开发人员)保持我们的理智(XAML不那么冗长)
- 业务逻辑(包括validation)保留在视图模型中,并且可以启用testing
祝你好运!
-Z
或者应该ViewModel属性公开实际的数据types,把这些杂事留给视图来处理?
-
转换和模板都是在
View
中完成的,因为它们只是将values
,models
和viewmodels
models
转换为controls
!Controls
仅在View
中可用。 -
validation是在
ViewModel
中完成的,因为validation是根据业务规则完成的,甚至可以通过调用远程服务来完成。 视图对业务规则一无所知,但知道如何呈现validation结果。
例如,如果在绑定到数字属性的文本框中input非数字值
正确制作的数字文本框控件决不允许用户input非数字值。