MVVM ViewModel应该执行类型转换/验证吗?

Tor*_*gen 38 validation wpf type-conversion mvvm

我们刚刚进入WPF中的MVVM.

我们已经使用我们在视图中绑定的"强类型"属性(int,double?等)实现了我们的ViewModel.

大多数类型转换都可以正常工作,因此输入数据非常简单.但是我们遇到验证问题.

例如,如果在绑定到数字属性的文本框中输入非数字值,则转换失败,永远不会设置该属性,并且我们永远不会有机会向用户提供正确的反馈.更糟糕的是,该属性保留其当前值,导致视图中显示的内容与ViewModel中实际显示的内容不匹配.

我知道,所有这些都可以通过价值转换器来处理.但我已经看到了一些意见,认为转换不应该是视图的责任.在视图中输入的是字符串,转换,验证等应该是ViewModel的职责(所以参数是这样).

如果是这样,我们应该将ViewModel上的大多数属性重写为字符串,并通过IErrorInfo接口提供错误信息.它肯定会在视图中使更简单,更精简的XAML.另一方面,从View设计者的角度来看,转换,验证等将不那么具有说明性,明确性和灵活性.

在我们看来,这两种方法根本不同,所以在我们决定之前,我们希望在这个问题上有一些明智的SO意见.

那么:ViewModels是否应该向视图公开一个简化的"基于文本"的界面并在内部处理转换?或者ViewModel属性是否应该公开实际的数据类型,将这些杂项留给视图来处理?

更新:

很难在这里挑选一个胜利者,但我终于找到了一个或多或少像我一样的人.

具体来说,我们决定保持键入ViewModel属性.其主要原因是它在视图设计中提供了灵活性,并且在XAML中具有显式,声明性转换/格式化的强大功能.

我注意到一个假设,你会在这方面不同意我们,视图的设计是固定的和准备好的.因此,不需要在视图中做出关于转换,格式化等的决定.但是我们的过程是一个敏捷的过程,而且我们还没有预先知道用户界面的所有细节.

事实上,在此过程中留下UI的细节留下了创造空间,而且,在我看来,即使是精心设计的设计也总是会在整个实施过程中变形.

所有这一切的重点在于,虽然业务规则实施当然属于ViewModel,但在我们看来,简单的转换和格式化是一种观点.这可能听起来像异端,但我实际上并不认为视图中的类型转换根本不需要单元测试(所以我们对实际的类型转换器进行单元测试).

总而言之,这是一个很好的讨论,伙计们,有良好的表达,知情的意见.谢谢.

Mar*_*ris 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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我将属性公开为int,因为它就是模型中的属性.您在问题中列出了这个缺点,但主要优点是它为视图的创建者提供了有关如何显示该数据的宝贵信息.请记住,我们(作为ViewModel的作者)不知道View的样子.通过提交数据为int的想法,View可以使用文本框或其他只接受数字(例如,拨号)的控件来显示信息.如果我们说我们要以一种我们认为对View有帮助的方式格式化数据,那么它就会消除它的重要力量.

另一方面,我们在现实世界中工作.我们倾向于知道观点是什么.我们很少在同一个ViewModel上插入和播放不同的视图,并且将转换代码添加到ViewModel中更加容易.我不认为这是对的,但这并不意味着你找不到我的生产代码......

最后(我确定你知道这一点,但是为了完成起见......)业务逻辑应该在ViewModel中完成.如果我们认为汽车不应超过70英里每小时,那么强制执行此操作并非责任​​.所以你仍然会得到某种错误提供者,但是在业务而不是显示级别.


好吧,也许那不是最后......

我想谈谈肯特的评论,我的想法不符合评论.

显然,我和肯特的观点(据我所知)之间的主要区别在于他将ViewModel作为视图模型读取,我将其视为将模型暴露给视图的东西.我承认一个微妙的区别,但我认为结果是我不想删除模型提供的信息,即使它使我正在使用的特定视图更容易.

我的观点是基于您应该能够交换视图的假设,它们应该是可能根据屏幕大小,硬件,平台,延迟和环境的要求而改变的稍纵即逝的事物.有趣的转折是我从来没有真正需要这个功能,也没见过任何曾经使用它的东西(除了概念证明应用程序),但是如果我们接受我们现在或将来的任何时候都不会使用它,并且每个视图模型将与一个,只有一个工作,查看那么我们不妨回去把所有的代码,在代码隐藏文件,并抛出视图模型进行彻底的-毕竟,它是如此紧密结合的,它可能还有是同一个班级.

我非常希望的情况下视图模型可以说"这个值是一个int,它永远是一个int,你可以以任何你喜欢的显示出来.不过你可以对我付出一切回来,我会尽我最好让它适合,如果我不能,我会让你知道".基本上我的MilesPerHour属性应该有一个int getter,但是一个对象setter.这样,视图会保留我认为需要的所有信息,但不必担心转换或验证.


Ken*_*art 8

绝对它属于视图模型,出于所有常见原因,包括:

  • 设计师拥有XAML.您是否希望设计人员必须理解并实现必需的类型转换和验证逻辑?
  • 可测性.您不想验证转换和验证逻辑是否正常工作?如果将它嵌入到视图中则要困难得多.

另一方面,从View设计者的角度来看,转换,验证等将不那么具有说明性,明确性和灵活性

我认为这是一个有争议的问题,因为视图设计师应该对这些事情负责.设计师正试图以一种特定的方式使UI看起来和感觉; 它是实现业务逻辑的开发人员,包括转换和验证逻辑.


Gre*_*g D 5

这是一个很好的问题,我当然可以看到讨论的双方.

我的想法是,你真正想要的是NumericInputControl你可以在你的xaml中使用的正确.这将提供更好的用户体验,因为您的用户将无法在数字字段中意外输入文本,并且由于控件在不验证输入的情况下限制输入,因此您可以维护更强类型的ViewModel.

我不确定你想要如何实现一个,我知道经典的微调器/ NumericUpDown控件因为它们不是触摸友好而失宠,但我不相信这样的介绍控件将违反设计方法或ViewModel的纯度.您将收到一个数字,然后您可以在适当的位置进行范围验证,通过IDataErrorInfo常规方式提供反馈,等等.:)这种技术可以让你在没有任何真正缺点的情况下获得两全其美(除了创建数字控件).


Zac*_*ham 5

MVVM ViewModel应该执行类型转换/验证吗?

是的.

视图模型是视图和模型之间的抽象层 - 执行任何类型转换的完美点(而不是繁琐的值转换器).验证应该绝对作为视图模型的一部分发生.

我们使用View Model来尽可能地处理数据类型的转换.这减少了对某些特定情况下的值转换器的需求.您希望公开视图要使用的最简单类型.这一直运作良好.

你提出的一个具体问题:

例如,如果在绑定到数字属性的文本框中输入非数字值,则转换失败,永远不会设置该属性,并且我们永远不会有机会向用户提供正确的反馈.更糟糕的是,该属性保留其当前值,导致视图中显示的内容与ViewModel中实际显示的内容不匹配.

可以通过将视图模型类型公开为可空类型来处理.即使输入了无效数据,这仍应允许更新基础源,并触发验证.这与我们使用DateTime和日期时间选择器的情况类似.

保持视野愚蠢.我们没有官方设计师,我们的开发人员是我们的设计师,所以保持视野愚蠢有一些好处:

  • 我们(开发人员)保持理智(XAML稍微冗长一点)
  • 业务逻辑(包括验证)保留在视图模型中并可以启用测试

祝好运!

-Z