具有数据注释的WPF验证

时间:2018-10-23 08:05:30

标签: wpf validation data-annotations

您好在用WPF中的数据注释进行验证

我在一个单独的项目中有一个类,因为我们正在多个项目中重用该类。几个字段都带有用于验证的数据注释。

验证类将数据注释返回的错误与一些自定义业务规则结合在一起,这些规则因客户而异

  internal class Validation<T> : ValidationRule where T : class, ICore
{
    IUnitofWork unitofWork;
    List<Func<T, bool>> CompiledRules = new List<Func<T, bool>>();
    List<Rule<T>> Rules = new List<Rule<T>>();
    Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();

    List<Func<Object, bool>> FieldRules = new List<Func<object, bool>>();
    public Validation()
    {
        unitofWork = new UnitOfWork();
        CompileRules();
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        return IsValid((T)value);
    }

    public  ValidationResult Validate(object value,string PropertyName ,CultureInfo cultureInfo)
    {
        return IsValid((T)value,PropertyName);
    }


    void CompileRules()
    {
        Type t = typeof(T);
        List<BusinessRule> rules = unitofWork.Repository<BusinessRule>().GetAll(b => b.Name == t.Name && b.Enabled == true);

        foreach (BusinessRule b in rules)
        {
            Func<T, bool> CompiledRule = CompileRule(b);
            CompiledRules.Add(CompiledRule);

            Rule<T> rule = new Rule<T>();
            rule.CompiledRule = CompiledRule;
            rule.ErrorMessage = b.MessageTemplate;
            rule.FieldName = b.Field;

            Rules.Add(rule);
        }
    }


    ValidationResult IsValid(T Value)
    {
        bool valid = true;
        _errors.Clear();

        if (CompiledRules.Count > 0 || Value.Errors.Count > 0)
        {
            //valid = CompiledRules.All(rule => rule(Value));


            foreach (Rule<T> r in Rules)
            {
                bool isValid = r.CompiledRule(Value);
                r.PassedRule = isValid;
                string field = r.FieldName;
                if (!isValid )
                {
                    valid = false;

                    string ErrorMessage = string.Format("{0} {1}", r.FieldName, r.ErrorMessage);



                    if (_errors.ContainsKey(field))
                        _errors[field].Add(ErrorMessage);
                    else
                    {
                        List<string> el = new List<string>();
                        el.Add(ErrorMessage);
                        _errors.Add(field, el);

                    }

                }
            }



        }


        ValidateAnnotations(Value,ref  valid);



        return new ValidationResult(valid, _errors);

             }


    void ValidateAnnotations(object Value,ref bool Valid)
    {


       DataAnnotationValidator annotationValidator = new DataAnnotationValidator();
       List< System.ComponentModel.DataAnnotations.ValidationResult> results =  annotationValidator.ValidateObject(Value);

        if (results.Count > 0)
        {
            Valid = false;
            foreach (System.ComponentModel.DataAnnotations.ValidationResult r in results)
            {
                if (_errors.ContainsKey(r.MemberNames.First()))
                {
                    _errors[r.MemberNames.First()].Add(r.ErrorMessage);
                }
                else
                {
                    List<string> propErrors = new List<string>();
                    propErrors.Add(r.ErrorMessage);
                    _errors.Add(r.MemberNames.First(), propErrors);
                }
            }
        }

    }

    void ValidateAnnotations(object Value,string PropertyName, ref bool Valid)
    {


        DataAnnotationValidator annotationValidator = new DataAnnotationValidator();
        List<System.ComponentModel.DataAnnotations.ValidationResult> results = annotationValidator.ValidateObject(Value, PropertyName);

        if (results.Count > 0)
        {
            Valid = false;
            foreach (System.ComponentModel.DataAnnotations.ValidationResult r in results)
            {
                if (_errors.ContainsKey(r.MemberNames.First()))
                {
                    _errors[r.MemberNames.First()].Add(r.ErrorMessage);
                }
                else
                {
                    List<string> propErrors = new List<string>();
                    propErrors.Add(r.ErrorMessage);
                    _errors.Add(r.MemberNames.First(), propErrors);
                }
            }
        }

    }

    ValidationResult IsValid(T Value, string PropertyName)
    {
        _errors.Remove(PropertyName);
        bool valid = true;

        if (CompiledRules.Count > 0)
        {
            //valid = CompiledRules.All(rule => rule(Value));

            foreach (Rule<T> r in Rules.Where(b=>b.FieldName == PropertyName))
            {
                bool isValid = r.CompiledRule(Value);
                r.PassedRule = isValid;
                string field = r.FieldName;
               // string field = "SelectedRow." + r.FieldName;
                if (!isValid)
                {
                    valid = false;

                    string ErrorMessage = string.Format("{0} {1}", r.FieldName, r.ErrorMessage);


                    if (_errors.ContainsKey(field))
                        _errors[field].Add(ErrorMessage);
                    else
                    {
                        List<string> el = new List<string>();
                        el.Add(ErrorMessage);
                        _errors.Add(field, el);

                    }
                }
            }



        }

        ValidateAnnotations(Value,PropertyName, ref valid);

        return new ValidationResult(valid, _errors);        
    }


    public Func<T, bool> CompileRule(BusinessRule r)
    {
        var paramT = Expression.Parameter(typeof(T));
        Expression expression = BuildExpr(r, paramT);

        return Expression.Lambda<Func<T, bool>>(expression, paramT).Compile();
    }
    static Expression BuildExpr(BusinessRule r, ParameterExpression param)
    {
        var left = MemberExpression.Property(param, r.Field);


        var tProp = typeof(T).GetProperty(r.Field).PropertyType;
        ExpressionType tBinary;
        // is the operator a known .NET operator?
        if (ExpressionType.TryParse(r.Operator, out tBinary))
        {
            var right = Expression.Constant(Convert.ChangeType(r.CompareValue, tProp));
            // use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
            return Expression.MakeBinary(tBinary, left, right);
        }
        else
        {
            var method = tProp.GetMethod(r.Operator);
            var tParam = method.GetParameters()[0].ParameterType;
            var right = Expression.Constant(Convert.ChangeType(r.CompareValue, tParam));
            // use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
            return Expression.Call(left, method, right);
        }
    }

}

internal class Rule<T> where T : class, ICore
{
    public string FieldName
    { get;set;  }
    public Func<T, bool> CompiledRule
    { get; set; }

    public Func<object,bool> RevisedRule
    { get; set; }
    public string ErrorMessage
    { get; set; }

    public bool PassedRule
    { get; set; }
}


internal class Error
    {

        public string ErrorMessage { get; set; }
        public string FieldName { get; set; }
    }

正在作为_errors列表使用,其中包含字段名称和与之相关的任何错误。

一旦有了这些,我们就会循环遍历并引发onPropertyErrorsChangedEvent

       internal void SetErrorDetails(ValidationResult Result)
    {
        propErrors = (Dictionary<string, List<string>>)Result.ErrorContent;

        foreach (string key in propErrors.Keys)
        {
            OnPropertyErrorsChanged(key);
        }

    }

在我看来,这两个字段是

            <TextBox Canvas.Left="138" 
                 Canvas.Top="75" 
                 FontFamily="Verdana"  
                 HorizontalAlignment="Left" 
                 Height="20"  
                 Text="{Binding OrganizationName,ValidatesOnDataErrors=True,NotifyOnValidationError=True,ValidatesOnNotifyDataErrors=True,ValidatesOnExceptions=True}"  VerticalAlignment="Top" Width="137" 
                 Validation.ErrorTemplate="{StaticResource ValidationTemplate }" 
                 Style="{StaticResource TextErrorStyle}"/>

        <TextBox Canvas.Left="138" 
                 Canvas.Top="225" 
                 FontFamily="Verdana" 
                 HorizontalAlignment="Left" 
                 Height="20"  
                 Text="{Binding SelectedRow.Postcode,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True,ValidatesOnExceptions=True}" 
                 VerticalAlignment="Top" 
                 Width="137" 
                 Validation.ErrorTemplate="{StaticResource ValidationTemplate }" 
                 Style="{StaticResource TextErrorStyle}" />

我遇到2个问题

当直接绑定到选定的行(邮政编码)时,在加载表单时会出现我的数据注释,但是当通过我的视图模型(组织名称)上的字段绑定时,它们不会出现。我们需要将它们绑定到视图模型上的字段,以便在验证过程中运行业务规则。

第二个问题,如果我使用组织的无效条目保存表单,则保存会停止,因为它无效,但是即使_errors中的属性存在错误,我也不会收到错误通知。

我不确定我在做错什么,有人可以向我指出正确的方向吗?

[编辑] 我们使用第三方文档服务来创建和显示视图

             void CreateDocument(object Arg)
    {
        string title = string.Empty;

        if (Arg.ToString().ToLower() == "edit" && SelectedRow !=null)
        {


            if (SelectedRow.OrganizationName != null)
                title = SelectedRow.OrganizationName;
        }
        else
        {
            SelectedRow = new Address();

            title = "New Address";
        }


        AddressDetailVM detail = new AddressDetailVM(SelectedRow,this);


      Document = iInternal.CreateDocument("AddressDetails",
                                                    detail,
                                                    title
                                                    );
        detail.Document = Document;
       // Document = iInternal.CreateDocument("AddressDetails", null, this, title);
        Document.Show();
    }

0 个答案:

没有答案