7

我有一个使用 MVVM 模式的应用程序,我想在用户填写信息时实现验证。

我想使用 IDataErrorInfo,但我不知道我的视图模型实现该接口是否是一个好主意,或者我创建一个新类是否更好。使用 IDataErrorInfo 和 MVVM 模式实施验证的最佳方法是什么?

编辑:我看到在某些示例中,实现在模型中(它与视图模型不同),但在我的情况下,模型基本上是我在创建 edmx 模型时从数据库创建的 POCO 实体实体框架,所以我想避免修改这个实体,因为如果我不需要更新我的模型,我将不得不再次做这项工作。

谢谢。

4

4 回答 4

2

如果您有一个保存数据的实体或自定义类型(如 Person、Student 等),那么您必须在您的实体或自定义类型中实现 IDataErrorInfo。假设您有一个允许输入学生数据的视图,并且您有它的 ViewModel StudentViewModel,并且此 ViewModel 具有 Student 类型的属性 Student,其属性(如 Name、Age 等)绑定到 View 的控件。要触发验证和更改以反映 UI,您必须在此Student中而不是在您的 ViewModel 中实现 IDataErrorInfo,并且您还必须在此类中实现INotifyPropertyChanged. 所以我的理解是,如果您的 ViewModel 中的属性很少,它们的类型(字符串和 ValueTypes)并绑定到 View,并且您想对它们应用验证,那么您必须在 ViewModel 中实现 IDataErrorInfo。如果你有 CustomType /Entity 那么你必须在那些不在 ViewModel 的类中实现接口。

它不是我的理解,如果您希望验证触发,则必须在其 EndProperties(如 Name,Age of Student )绑定到 View 控件的类中实现 IDataErrorInfo 和 INotifyPropertyChanged 。

    //Interface which has fields of Student class on which ValidationAttribute are to be applied
public interface IStudent
{
    [Required(ErrorMessage = "Name is required")]
    string Name { get; set; }

    [Range(10, 90, ErrorMessage = "Age should be between 10 and 90")]
    int Age { get; set; }
}
//Partial Class to implement IStudent
public partial class Student : IStudent
{

}

//POCO
public partial class Student : INotifyPropertyChanged
{
    private string name;
    private int age;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;

            Notify("Name");
        }
    }

    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;

            Notify("Age");
        }
    }
    private void Notify(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
于 2013-07-23T15:39:26.510 回答
2

将验证逻辑与 UI 分开总是一个好主意。这样,使用 IDataErrorInfo 是正确的。

在视图模型和模型之间,我更喜欢在视图模型上实现 IDataErrorInfo,因为这个接口被 UI 使用。您可以通过直接在测试代码中调用索引器来模拟 UI,但是如果您确实需要业务逻辑层中的验证逻辑,那么这样的调用没有多大意义。

在我们的项目中,validation是一个比较独立的组件,可以通过配置同时供表现层和业务逻辑层使用。从视图模型的角度来看,它非常薄,只包含一个调用和在索引器内部构造验证结果。

此外,另一个考虑因素是 INotifyDataErrorInfo,它由 .Net 4.5 和 Silverlight 提供。它为一个耗时的验证提供了来自一个属性的更多验证结果和异步验证,这是我们计划更新到 .Net 4.5 后想要的。

希望它可以帮助你。

于 2013-07-23T15:42:56.917 回答
2

我同意关于这个主题的绝大多数评论,但我回答是提供我对这个界面的“升级”。

我看到的IDataErrorInfo界面问题是它一次只能解决一个错误。BaseDataType因此,我在我的类(我的所有数据类型的基类)中添加了一个额外的字段:

protected ObservableCollection<string> errors = new ObservableCollection<string>();

然后我添加了以下属性:

// this just enables me to add into the error collection from outside this class
public ObservableCollection<string> ExternalErrors
{
    get { return externalErrors; }
}

public override ObservableCollection<string> Errors
{
    get
    {
        errors = new ObservableCollection<string>();
        // add properties to validate
        errors.AddUniqueIfNotEmpty(this["Property1ToValidate"]);
        errors.AddUniqueIfNotEmpty(this["Property2ToValidate"]);
        errors.AddUniqueIfNotEmpty(this["Property3ToValidate"]);
        // add external errors (from view models)
        errors.AddRange(ExternalErrors);
        return errors;
    }
}

public virtual bool HasError
{
    get { return Errors != null && Errors.Count > 0; }
}

AddUniqueIfNotEmpty方法是一种扩展方法,相信大家都能猜到它的作用。

使用它,我可以直接绑定到视图中的错误集合,甚至更好的是,HasError使用 a 绑定到属性BoolToVisibilityConverter,以便在集合为空时隐藏显示错误的控件。

于 2013-07-23T16:32:04.517 回答
1
<TextBox Text="{Binding Path=MyCoolProperty, ValidationOnDataErrors=true}"

也许我错过了一些东西,但如果你有这样的绑定——你的“MyCoolProperty”类必须实现 INotifyPropertyChanges 和 IDataErrorInfo——否则它将无法工作。

所以我想说的问题不是:“应该实现 IDataErrorInfo”,而是如何实现 IDataErrorInfo

于 2013-07-25T05:56:09.050 回答