根据属性的有效性调用方法时,正确的数据验证方法

时间:2014-08-26 05:40:18

标签: c# wpf idataerrorinfo

我已经看到了两种在.Net 4.0中进行数据验证的方法。目前我使用像这样的IDataErrorInfo,但这里的问题是"这个"只有在调用Age属性的getter时才调用IDataErrorInfo。我在setter中有一些逻辑,它依赖于Age的有效值。

public int Age
    {
        get { return _age; }
        set
        {
            _age = value;
            OnPropertyChanged("Age");
            //My methods to call when Age is valid

        }
    }

    public string this[string columnName]
    {
        get
        {
            string error = null;

            switch (columnName)
            {
                case "Age":
                    if (Age < 10 || Age > 100)
                      error="The age must be between 10 and 100";
                    break;
            }
            return error;

        }
    }

我无法调用这些方法,因为调用setter时没有进行验证,只有在调用getter时才会进行验证。有人可以帮助我理解为什么它的设计就像那样。在我看来,验证应该接近setter其中赋值。

我必须使用异常验证

将我的实现更改为此
public int Age
    {
        get { return _age; }
        set
        {
            if (value < 10 || value > 100)
                throw new ArgumentException("The age must be between 10 and 100");
            _age = value;

            //My methods to call when Age is valid
        }
    }

我问这个问题,因为现有的实现基于我项目中的IDataErrorInfo,并且有很多属性得到验证。有一些解决方案可以让我保持IDataerrorInfo实现并实现与实现异常验证相同的效果。

我不想在我的属性设置器上抛出异常,因为它是microsoft的设计指南

  

如果可能,请不要将异常用于正常的控制流程。除了   对于系统故障和具有潜在竞争条件的操作,   框架设计者应该设计API,以便用户可以编写代码   这不会引发异常。例如,您可以提供一种方法   在调用成员之前检查前提条件,以便用户可以编写   不抛出异常的代码。

并且我也不想抛出我在视图模型代码中没有捕获的异常。

这是我的xaml

  <TextBox Text="{Binding Age, ValidatesOnDataErrors=True,
      NotifyOnValidationError=True, ValidatesOnExceptions=True,
    UpdateSourceTrigger=PropertyChanged}"/>

3 个答案:

答案 0 :(得分:2)

绑定首先设置值,然后调用getter aaand然后调用indexer进行验证。但是,您的问题是您希望索引器首先出现。这不是wpf的工作原理。

基本上,wpf在使用IDataErrorInfo时为您提供了所有自由,因此您必须自己实现所有。让我给你举个例子:

public int Age
{
    get { return _age; }
    set
    {
        int oldVal = _age;
        _age = value;
        if(Validate("Age") == null)
        {
           // Do whatever you want

           OnPropertyChanged("Age");
        }
        else
        {
            // You can rollback value or not, it wouldnt matter...
            // PropertyChanged will not be fired!!!
            _age = oldVal;
        }
    }
}

public string this[string columnName]
{
    get
    {
       return Validate(columnName);
    }
}

public string Validate(string propertyName)
{
    string error = null;
    switch (propertyName)
    {
        case "Age":
            if (_age< 10 || _age > 100)
                error = "The age must be between 10 and 100";
    }

    return error;
}

答案 1 :(得分:0)

在我看来,实现这一目标的最佳方法是在setter中调用check方法,如果验证结果为false,则不要将value设置为property并设置一些“InvalidState”标志。否则设置值并继续。

使用InvalidState标志,当调用任何取决于对象状态的方法时,可以抛出InvalidOperationException

您可以使用基本的布尔值InvalidState,或者您可以将枚举用于不同的状态( NotInitialized 有效无效等)来管理代码流。

答案 2 :(得分:0)

这里的问题是由于你的设计而不是在.NET或WPF中完成的任何事情。

根据它的声音,您会立即根据代码中的OnPropertyChanged()调用更新内容。在使用涉及用户的UI绑定元素时,我不会这样做,因为人类是任何系统中的弱点,并且您总是会在某个时刻从用户那里获得无效输入。

您应该通过保存命令而不是基于OnPropertyChanged()的持续更新来执行确定性更新。表单验证的要点是,在将数据提交到底层结构之前,您可以确定性地验证整个数据集。

您应该执行类似添加私有Boolean变量以存储Age属性的验证状态并通过Command方法启用/禁用保存ICommand.CanExecute()的操作

相关问题