我们刚刚进入WPF中的MVVM。
我们已经使用我们在视图中绑定的“强类型”属性(int,double?等)实现了我们的ViewModel。
类型转换大多数都可以正常工作,因此输入数据非常简单。但是我们遇到验证问题。
例如,如果在绑定到数字属性的文本框中输入非数字值,则转换失败,永远不会设置该属性,并且我们永远不会有机会向用户提供正确的反馈。更糟糕的是,该属性保留其当前值,导致视图中显示的内容与ViewModel中实际内容之间不匹配。
我知道,所有这些都可以通过价值转换器来处理。但我已经看到了一些意见,认为转换不应该是视图的责任。在视图中输入的是字符串,转换,验证等应该是ViewModel的职责(所以参数是这样)。
如果是这样,我们应该将ViewModel上的大多数属性重写为字符串,并通过IErrorInfo接口提供错误信息。它肯定会在视图中使更简单,更精简的XAML。另一方面,从View设计师的角度来看,转换,验证等将不那么具有说明性,明确性和灵活性。
在我们看来,这两种方法根本不同,所以在我们决定之前,我们希望在这个问题上有一些明智的意见。
那么:ViewModels是否应该在视图中公开一个简化的“基于文本”的界面并在内部处理转换?或者ViewModel属性是否应公开实际数据类型,将这些杂项留给视图来处理?
更新
很难在这里挑选一名获胜者,但我终于找到了一个或多或少与我相似的人。
具体来说,我们决定保持键入ViewModel属性。这样做的主要原因是它为视图设计提供了灵活性,以及XAML中显式,声明性转换/格式化的强大功能。
我注意到一个假设,你在这方面不同意我们,视图的设计是固定的并准备好了。因此,不需要在视图中做出关于转换,格式化等的决定。但是我们的过程是一个敏捷的过程,而且我们还没有预先知道用户界面的所有细节。
事实上,在此过程中留下UI的细节留下了创造空间,而且在我看来,即使是精心设计的设计也总是会在整个实施过程中变形。
所有这一切的要点是,虽然业务规则实施当然属于ViewModel,但在我们看来,简单的转换和格式化是一种观点。这可能听起来像异端,但我实际上并不认为视图中的类型转换根本不需要单元测试(所以我们对实际的类型转换器进行单元测试)。
总而言之,这是一个很好的讨论,伙计们,有着良好的表达,知情的意见。感谢。
答案 0 :(得分:13)
这是一个非常有趣的问题,我不觉得有一个明确的答案,但我会尽力把我的想法抛在那里。
按照我的理解来看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,因为它就是模型中的属性。您在问题中列出了这个缺点,但主要优点是它为视图的创建者提供了有关如何显示该数据的宝贵信息。请记住,我们(作为ViewModel的作者)不知道View的样子。通过提交数据为int的想法,View可以使用文本框或其他只接受数字(例如,拨号)的控件来显示信息。如果我们说我们将以一种我们假设对视图有帮助的方式格式化数据,那么它就会消除它的重要力量。
另一方面,我们在现实世界中工作。我们倾向于知道观点是什么。我们很少在同一个ViewModel上插入和播放不同的视图,并且将转换代码添加到ViewModel中更加容易。我不认为这是对的,但这并不意味着你找不到我的生产代码......
最后(我确定你知道这一点,但是为了完成起见......)业务逻辑应该在ViewModel中完成。如果我们认为汽车不应超过70英里每小时,那么强制执行此操作并非责任。所以你仍然会得到某种错误提供者,但是在业务而不是显示级别。
好吧,也许那不是最终......
我想解决肯特的评论,我的想法不符合评论。
显然,我和Kent的观点(据我所知)之间的主要区别在于他将ViewModel作为视图模型进行读取,并将其读取为将模型暴露给View的事物。我承认一个微妙的区别,但我认为结果是我不想删除模型提供的信息,即使它使我正在使用的特定视图更容易。
我的观点是基于您应该能够交换视图的假设,它们应该是可能根据屏幕大小,硬件,平台,延迟和环境的要求而改变的稍纵即逝的事物。有趣的是,我有从不实际上需要这个功能,也没有看过任何曾经使用它的东西(除了概念证明应用程序),但是如果我们接受我们现在或者不会使用它未来的任何一点,每个ViewModel都可以使用一个,只有一个View,那么我们也可以回到将所有代码放在代码隐藏文件中并完全抛出ViewModel - 毕竟,它是如此紧密耦合,它可能也是同一类。
理想情况下,我希望ViewModel可以说“这个值是一个int,它总是一个int,你可以随意显示它。但是你可以给我任何回报而且我可以我会尽力使它健康,如果我不能,我会让你知道“。基本上我的MilesPerHour属性应该有一个int getter,但是一个对象setter。这样,视图会保留我认为需要的所有信息,但不必担心转换或验证。
答案 1 :(得分:8)
由于所有常见原因,它绝对属于视图模型,包括:
另一方面,从View设计师的角度来看,转换,验证等将不那么具有说明性,明确性和灵活性
我认为这是一个有争议的问题,因为视图设计师应该对这些事情负责。设计师正试图以一种特定的方式使UI看起来和感觉;它是实现业务逻辑的开发人员,包括转换和验证逻辑。
答案 2 :(得分:5)
这是一个很好的问题,我当然可以看到讨论的双方。
我的想法是,你真正想要的是你可以在你的xaml中使用的NumericInputControl
。这将提供更好的用户体验,因为您的用户将无法在数字字段中意外输入文本,并且由于控件在不验证输入的情况下限制输入,因此您可以维护更强类型的ViewModel。
我不确定你想要实现一个,我知道经典的微调器/ NumericUpDown控件不受欢迎,因为它们不是触摸友好的,但我不相信引入这样的控件将违反设计方法或ViewModel的纯度。您将收到一个号码,然后您可以在适当的位置进行范围验证,像往常一样通过IDataErrorInfo
提供反馈,等等。 :)这种技术可以让你充分利用这两个世界,没有任何真正的缺点(除了创建一个数字控件)。
答案 3 :(得分:5)
MVVM ViewModel应该执行类型 转换/验证?
是强>
视图模型是视图和模型之间的抽象层 - 执行任何类型转换(而不是繁琐的值转换器)的最佳位置。验证应该绝对作为视图模型的一部分发生。
我们使用View Model来尽可能地处理数据类型的转换。这减少了对某些特定情况下的值转换器的需求。您希望公开视图要使用的最简单类型。这一直运作良好。
您提出的一个具体问题:
例如,如果是非数字值 在一个绑定到的文本框中输入 数字属性,转换 失败,财产从未设置,并且 我们永远不会有机会提供 适当的反馈给用户。更差, 该物业保留其当前 价值,导致不匹配 视图中显示的内容和 ViewModel中的实际内容。
可以通过将您的视图模型类型公开为可为空的类型来处理。即使输入了无效数据,这仍应允许更新基础源,并触发验证。这与我们使用DateTime和日期时间选择器的情况类似。
保持视野愚蠢。我们没有官方设计师,我们的开发人员是我们的设计师,因此保持观点愚蠢有一些好处:
祝你好运!
-Z
答案 4 :(得分:0)
或者ViewModel属性是否应公开实际数据类型,将这些杂项留给视图来处理?
转化和模板已在View
中完成,因为它们都只是values
的转换,{{1} }和models
进入viewmodels
! controls
仅在Controls
内可用。
验证在View
中完成,因为验证是根据业务规则完成的,甚至可以通过调用远程服务来完成。 View对业务规则一无所知,但知道如何呈现验证结果。
例如,如果在绑定到数字属性的文本框中输入了非数字值
精心设计的数字文本框控件永远不允许用户输入非数字值。