c# - 在 ViewModel 实体上使用 DataAnnotation 进行 Prism IDataErrorInfo 验证

标签 c# validation mvvm prism idataerrorinfo

我正在使用 Prism MVVM 框架在 WPF 中实现数据验证。我在绑定(bind)到表示层的 ViewModel 中使用干净的数据实体。

 <TextBox Text="{Binding User.Email, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />

我在基 ViewModel 类中实现了 IDataErrorInfo 的通用实现,该基类针对我的实体(在本例中为用户)上的 DataAnnotation 属性运行验证。

问题是,当绑定(bind)到一个实体时,WPF 框架会在实体上查找 IDataErrorInfo,而不是我希望此逻辑存在的 ViewModel。如果我用我的 ViewModel 中的属性包装我的实体,那么一切正常,但我不想妥协在 ViewModel 中使用实体。

有没有办法告诉 WPF 在 ViewModel 中查找 IDataErrorInfo 而不是正在绑定(bind)的子对象?

谢谢, 迈克

最佳答案

我选择的选项是在一个由所有 ViewModel 和实体扩展的基类中显式实现 IDataErrorInfo。这似乎是让 WPF 顺利进行的最佳折衷方案,并且至少对调用者隐藏了 IDataErrorInfo 的实现,因此它们至少看起来很干净。我公开了一个 protected ValidateProperty,如果需要,可以在子类中覆盖任何自定义行为(例如密码/密码确认场景)。

public abstract class DataErrorInfo : IDataErrorInfo
{
    string IDataErrorInfo.Error
    {
        get { return null; }
    }

    string IDataErrorInfo.this[string columnName]
    {
        get { return ValidateProperty(columnName); }
    }

    protected virtual string ValidateProperty(string columnName)
    {
         // get cached property accessors
            var propertyGetters = GetPropertyGetterLookups(GetType());

            if (propertyGetters.ContainsKey(columnName))
            {
                // read value of given property
                var value = propertyGetters[columnName](this);

                // run validation
                var results = new List<ValidationResult>();
                var vc = new ValidationContext(this, null, null) { MemberName = columnName };
                Validator.TryValidateProperty(value, vc, results);

                // transpose results
                var errors = Array.ConvertAll(results.ToArray(), o => o.ErrorMessage);
                return string.Join(Environment.NewLine, errors);
            }
            return string.Empty;
    }

    private static readonly Dictionary<string, object> PropertyLookupCache =
        new Dictionary<string, object>();

    private static Dictionary<string, Func<object, object>> GetPropertyGetterLookups(Type objType)
    {
        var key = objType.FullName ?? "";
        if (!PropertyLookupCache.ContainsKey(key))
        {
            var o = objType.GetProperties()
            .Where(p => GetValidations(p).Length != 0)
            .ToDictionary(p => p.Name, CreatePropertyGetter);

            PropertyLookupCache[key] = o;
            return o;
        }
        return (Dictionary<string, Func<object, object>>)PropertyLookupCache[key];
    }

    private static Func<object, object> CreatePropertyGetter(PropertyInfo propertyInfo)
    {
        var instanceParameter = Expression.Parameter(typeof(object), "instance");

        var expression = Expression.Lambda<Func<object, object>>(
            Expression.ConvertChecked(
                Expression.MakeMemberAccess(
                    Expression.ConvertChecked(instanceParameter, propertyInfo.DeclaringType),
                    propertyInfo),
                typeof(object)),
            instanceParameter);

        var compiledExpression = expression.Compile();

        return compiledExpression;
    }

    private static ValidationAttribute[] GetValidations(PropertyInfo property)
    {
        return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
    }


}

关于c# - 在 ViewModel 实体上使用 DataAnnotation 进行 Prism IDataErrorInfo 验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3739059/

相关文章:

c# - 无法将字符串转换为 DateTime

c# - C#中如何通过所有文件访问一个对象

php - Laravel:更新和创建时唯一的验证

具有 where 条件的 C# LINQ GroupBy

c# - 转换器没有被调用

c# - mvvm一般的疑惑和messageBox

c# - 如何将 List<string> 转换为所需格式的数据集

c# - 验证 ASP.Net 文本框不为空但允许空格?

c# - 如何在WPF中对GridViewColumns进行分组,以便扩展器填充整个宽度

c# - 在 TextBlock 的 CoerceValueCallback 中添加内联时出现 FatalExecutionEngineError