对于我的WPF-Application,我决定使用MVVM。这是我的概念,我将如何实现这种模式。
我的第一个问题是关于包装还是不在ViewModel中包装我的模型。
ViewModel
应该出于各种原因包装模型:
我不喜欢直接绑定到Model(DateTime,int,...)中的强类型属性,因为当我这样做时=> WPF控制我对这些类型的验证。这真的很糟糕,因为当用户在Datepicker中编写'aaaa'时,我的模型是有效的(我的模型永远不会知道,因为WPF控制强类型属性)并且启用了保存按钮 - 这确实是错误的。
我没有将我的模型的所有属性暴露给视图,我的ViewModel
应该保护我的模型(我有一些属性,应该只在表示层有getter而没有setter)我的决定是,ViewModel
绝对应该包装模型。所以ViewModel
实施INotifyPropertyChanged
。
但现在我的业务验证存在问题。
当我采用漂亮的IDataErrorInfo,然后我在ViewModel中拥有整个业务规则,这打破了我的概念。业务规则绝对应该在模型中。
示例:当用户选择类型A时,则字段1和字段2是必需的。当用户选择类型B时,则字段3是必需的 - 该字段应标记为红色,并且保存按钮在无效时禁用。还有更重的东西,如免费/占用DateTime-Ranges。
当我在ViewModel中执行此操作时,这绝对是不好的,因为大多数事情都是业务部分。
那我怎么能做到这一点?
在片刻我有这个解决方法:
所有ValidationRules
都在模型中作为简单方法,例如
public string ValidateBirthday(string birthay)
{
if (...)
{
return "Birthday should be…";
}
return string.Empty;
}
在我的ViewModel中,我实现了IDataErrorInfo,并重定向到我的模型验证,如下所示:
public string this[string columnName]
{
get
{
switch (columnName)
{
case "Birthday":
return Model.ValidateBirthday(Birthday);
case "XXX":
return Model.ValidateXXX(XXX);
case "YYY":
return Model.ValidateYYY(YYY);
break;
}
}
}
我从来没有在一个例子中看到这样的东西(重定向到模型),所以我对我的实现非常怀疑。
我的解决方法是否正常,或者您是否看到有任何问题?
我试着提供更多关于我的意思的信息......
我知道Model中的实现INotifyPropertyChanged和IDataErrorInfo。
这适用于从View到Model的直接绑定。
public PersonViewModel(Person person)
{
PersonModel = person;
}
…
}视图:
<DatePicker Text="{Binding PersonModel.Birthday}"/>
最大的缺点是:WPF控制所有强类型属性。
示例:用户在datepicker中键入07/20/2008,因此PersonModel将被通知,PersonModel可以检查此信息,当OK,然后PersonModel有效=> SaveButton启用。
现在用户在日期选择器中键入“aaa”,WPF将控制此验证,因为它是对强类型属性(DateTime)的绑定。 PersonModel不会被告知,所以PersonModel仍然有效=> SaveButton是启用的!
所以对于那个'问题',我需要正确的ViewModel
。
public string Birthday
{
get
{
if (_personModel. Birthday!= null)
{
return ((DateTime) _personModel. Birthday).ToShortDateString();
}
else
{
return String.Empty;
}
}
set
{
if (_personModel. Birthday.ToString() != value)
{
DateTime dateValue;
if (DateTime.TryParse(value, out dateValue))
{
_personModel.Birthday = dateValue;
…
}
else
{
…
}
}
}
}
public PersonViewModel(Person person)
{
_personModel = person;
}
…
}现在我不直接从View绑定模型。我绑定了包装Model的ViewModel中的Properties。
<DatePicker Text="{Binding Birthday}"/>
最大的优点是:现在我可以完全控制用户在字段中输入的内容。当用户在Datepicker中键入类似'aaa'的字符串时,我可以捕获这个=>将状态设置为无效并禁用SaveButton。
这是一个原因,为什么我不采用从View到Model的直接绑定。其他原因是readonly Property。在Model I中,我已经获取并设置了每个Property,但是为了安全问题,我将不会使用get和set来提供Model中的所有Properties。因此,ViewModel也可以通过仅使用get包装此属性来解决此问题。通过直接绑定,你不能做所有这些事情。
我的观点是,我肯定会在ViewModel中包装模型中的所有属性,但是如何在模型中使用漂亮的IDataErrorInfo
(它只适用于直接绑定)?
您混合的两个概念是:业务对象和验证。
现在几乎每个系统都使用客户端 - 服务器架构,即使它是一个独立的应用程序。
在这种情况下,您有两个验证位置:
也:
您的模型对象不应该有任何逻辑,因为您将使用某些代码破坏它们,在某些时候您需要重用它们。
如此处所述,您应该将该验证逻辑分离为仅了解该对象以及如何验证它们的服务。这样,您就可以使用UI中的验证服务。
您的Save
按钮应仅对UI更改做出反应,您只能从ViewModel获取。
基本上,您将在这里应用SOLID principles:每个层都有非常明确的职责(模型 - >数据,服务 - >验证,dto - >为客户端准备的数据,viewmodels - > UI交互)。所有代码都易于使用,易于扩展且易于重构。
第一和第二个问题:UI仅验证输入:数字字段中没有随机字符,文本字段中没有sql字符,日期格式正确等。
认为“如果那样那么”应该由后端处理,正如您所描述的:
第三个问题:这对我来说是正确的。
第四个问题:DTO只是一个概念,您可以使用通过WCF进行通信的真实后端服务器,或者您可以拥有一堆充当服务但在同一应用程序域中调用的类(与任何其他项目引用一样) 。在任何一种情况下,您都可以选择要发送和接收的数据。
你应该开始朝那个方向发展,然后看看哪个更适合你。