【问题标题】:IDataErrorInfo : How to know if all properties are valid?IDataErrorInfo:如何知道所有属性是否有效?
【发布时间】:2012-11-28 10:47:56
【问题描述】:

我有一个WPF 应用程序(.Net 3.5),它使用 ViewModel 上的IDataErrorInfo 来验证输入。

效果很好,用户控件得到了正确的 UI 反馈。

问题是用户仍然可以更改所选元素,或保存此元素。

所以我的问题是:我怎么知道我的所有属性都是有效的?或者至少我所有显示的值都是有效的。目标是在这个结果上绑定一些IsActive

【问题讨论】:

  • 如果errors属性不为null或为空则报错
  • 您想在哪里知道它们是否都有效?在 View 还是 ViewModel 中?
  • 我认为这个问题的答案可能会对你有所帮助:stackoverflow.com/questions/104520/…
  • @BobVale 是的,我知道,但我想一次验证所有属性
  • @Blachshma 在视图模型中或视图中,但是我需要在视图中绑定一个属性来绑定整个数据模型,而不仅仅是一个字段

标签: wpf idataerrorinfo validation


【解决方案1】:

根据您对IDataErrorInfo 实施的评论,将您的实施更改为这种风格......

#region IDataErrorInfo Members

public string Error
{
    get { return this[null] }
}

public string this[string columnName]
{
    get
    {
        StringBuilder result = new StringBuilder();
        if (string.IsNullOrEmpty(columnName) || columnName == "FirstName")
        {
            if (string.IsNullOrEmpty(FirstName))
                result.Append("Please enter a First Name\n");
        }
        if (string.IsNullOrEmpty(columnName) || columnName == "LastName")
        {
            if (string.IsNullOrEmpty(LastName))
                result.Append("Please enter a Last Name\n");
        }
       if (string.IsNullOrEmpty(columnName) || columnName == "Age")
        {
            if (Age < = 0 || Age >= 99)
                result.Append("Please enter a valid age\n");
        }
        return (result.Length==0) ? null : result.Remove(result.Length-1,1).ToString();
    }
}

#endregion


public bool IsValid {
   get { return string.IsNullOrEmpty(this.Error); }
}

然后在您的属性更改事件中

if (e.PropertyName == "Error") {
   OnPropertyChanged(this,new PropertyChangedEventArgs("IsValid"));
}
if (e.PropertyName != "Error" && e.PropertyName != "IsValid") {
   OnPropertyChanged(this,new PropertyChangedEventArgs("Error"));
}

【讨论】:

  • 哦,这就是错误的原因?我不明白在我发现的所有示例之前和所有示例中,这都没有实现
  • @J4N 是的,错误应该是对对象本身有什么问题的总结,通常作为这个答案中的总结来完成。
  • 我修改了我的代码以使用你的解决方案(几乎,我还有一个包含布尔值的 IsValid 属性,我在我的事件更改元素中更新它。
  • 这很好用!谢谢!一个小细节:它是 ((IDataErrorInfo)this)[null] 而不仅仅是 this[null]。 ((IDataErrorInfo)this) 相同。“IsValid”检查中的错误。我还将 this[] 函数中的 1 行 return 语句重写为 8 行以使其更清晰。 :P
  • @SimonF 我认为只有在显式实现接口时才需要强制转换,即string IDataErrorInfo.Error {
【解决方案2】:

现在,我在我的模型上添加了这个方法。

    public Boolean IsModelValid()
    {
        Boolean isValid = true;
        PropertyInfo[] properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (PropertyInfo p in properties)
        {
            if (!p.CanWrite || !p.CanRead)
            {
                continue;
            }
            if (this[p.Name] != null)
            {
                isValid = false;
            }
        }
        return isValid;
    }

我在 PropertyChanged 事件上绑定了对象本身,

    public MyClassName()
    {
        PropertyChanged += CheckModelValidity;
        CheckModelValidity(null, null);
    }

当它改变时,我调用这个方法,如果结果与我实际的公共成员不同,我更新它:

    private void CheckModelValidity(object sender, PropertyChangedEventArgs e)
    {
        bool isModelValid = IsModelValid();
        if(isModelValid!= IsValid)
        {
            IsValid = isModelValid;
        }
    }

然后我可以绑定 IsValid 属性。

不知道有没有更好的解决方案?

【讨论】:

  • 如果您打算沿着这条路线走,请在静态构造函数中初始化有效属性名称列表,并将其存储在静态字段中。然后你只发生一次反射
  • @BobVale 是的,我也不为此感到自豪,但我更喜欢这样做而不是有人忘记做一次检查的风险。很好,我将在静态变量中初始化它。
  • 这不会重新评估错误。您可能希望这样做:Validator.TryValidateObject(this, new ValidationContext(this, null), null); 返回整体验证(包括错误)是否成功。
【解决方案3】:

感谢 Bob Vale 的创意! 我注意到我有几个模型并且代码非常重复。我创建了这个基类:

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;

namespace Core.Models
{
    public abstract class ValidatableObject : IDataErrorInfo
    {
        public virtual bool IsValid() => this[null] == null;

        [NotMapped]
        public string? Error => this[null];

        [NotMapped]
        protected abstract (string, Func<string?>)[] Checks { get; }

        public virtual string? this[string? name] => validate(name, Checks);

        private static string? validate(string? name, params (string, Func<string?>)[] checks)
        {
            StringBuilder results = new();

            foreach ((string val, Func<string?> check) in checks)
            {
                if (String.IsNullOrEmpty(name) || name == val)
                {
                    string? result = check();
                    if (result != null)
                        results.AppendLine(result);
                }
            }

            return results.Length == 0
                ? null
                : results.ToString(0, results.Length - Environment.NewLine.Length);
        }
    }
}

这是用法:

    private class Validatable : ValidatableObject
    {
        public string Email { get; set; }
        public string Comment { get; set; }

        protected override (string, Func<string?>)[] Checks => new (string, Func<string?>)[]
        {
            (nameof(Email), () => Validate.Email(Email)),
            (nameof(Comment), () => Validate.LengthOfOptionalString(Comment)),
        };
    }

【讨论】:

    猜你喜欢
    • 2020-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 2013-10-17
    • 1970-01-01
    相关资源
    最近更新 更多