WPF的验证方案

来源:互联网 发布:linux rpm 卸载 编辑:程序博客网 时间:2024/06/05 04:43

今天在别人的博客上看到这个,转过来一下

想想以后会用得着。。。

 

1.通过继承ValidationRule抽象类,定义验证规则重写Validate方法

这里是sdk的示例代码

  1. public class AgeRangeRule : ValidationRule
  2.     {
  3.         private int _min;
  4.         private int _max;
  5.         public AgeRangeRule()
  6.         {
  7.         }
  8.         public int Min
  9.         {
  10.             get { return _min; }
  11.             set { _min = value; }
  12.         }
  13.         public int Max
  14.         {
  15.             get { return _max; }
  16.             set { _max = value; }
  17.         }
  18.         public override ValidationResult Validate(object value, CultureInfo cultureInfo)
  19.         {
  20.             int age = 0;
  21.             try
  22.             {
  23.                 if (((string)value).Length > 0)
  24.                     age = Int32.Parse((String)value);
  25.             }
  26.             catch (Exception e)
  27.             {
  28.                 return new ValidationResult(false"Illegal characters or " + e.Message);
  29.             }
  30.             if ((age < Min) || (age > Max))
  31.             {
  32.                 return new ValidationResult(false,
  33.                   "Please enter an age in the range: " + Min + " - " + Max + ".");
  34.             }
  35.             else
  36.             {
  37.                 return new ValidationResult(truenull);
  38.             }
  39.         }
  40.     }

 

然后是xaml的使用代码

  1.  <TextBox Name="textBox1" Width="50" FontSize="15"
  2.              Validation.ErrorTemplate="{StaticResource validationTemplate}"
  3.              Style="{StaticResource textBoxInError}"
  4.              Grid.Row="1" Grid.Column="1" Margin="2">
  5.       <TextBox.Text>
  6.         <Binding Path="Age" Source="{StaticResource ods}"
  7.                  UpdateSourceTrigger="PropertyChanged" >
  8.           <Binding.ValidationRules>
  9.             <c:AgeRangeRule Min="21" Max="130"/>
  10.           </Binding.ValidationRules>
  11.         </Binding>
  12.       </TextBox.Text>
  13.     </TextBox>

 

就是通过ValidationRules集合挂验证规则.如果逻辑不复杂的话,勉强可以接受,这里我个人认为可以作为界面的验证.但如果业务逻辑一旦发生变化,则意味着xaml文件一直需要修改,这并不是一种好的做法,并不推荐.(而且内置的验证规则太少了,有的话还勉强用用,都需要自己重写)

2.通过正则表达式附加属性简化写法,大家应该知道,验证部分,正则表达式占了很大部分.

定义一个正则表达式类,然后通过附加属性进行验证.具体的方案,请参考这里
http://www.codeproject.com/WPF/RegexValidationInWPF.asp
然后前端xaml的使用方法

  1.  <TextBox
  2.       Text="{Binding Path=EmailAddress, UpdateSourceTrigger=PropertyChanged}" 
  3.       jas:RegexValidator.RegexText="^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+/.[a-zA-Z]{2,4}$" 
  4.       jas:RegexValidator.ErrorMessage="Invalid e-mail address." 
  5.       />

代码是简化了,但有个缺点,只能定义一个验证规则,也存在着我上面提到的问题,把正则写在ui上面,并不是一个好的做法,应该对其进行封装(看着这么多符号就心慌,高手可以这么做),也并不推荐的做法.

3.与业务逻辑验证绑定在一起
这种做法与上面的都不同,因为业务逻辑的判断与常规的判断比如(比如是否必填,字符匹配等),而且前端只需要绑定字段就可以了,先看前端的做法

  1. <TextBox Width="100" Grid.Column="1" HorizontalAlignment="Left" Margin="5,5,0,5" x:Name="txtFirstName" Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True}" ToolTip="Enter customers first name." />

 

请注意,这里并没有写什么验证规则或正则表达式哦,真正的验证在这里,验证的对象必须实现IDataErrorInfo接口,通过索引器,可以判断对象的每个字段.
上面紧紧是FirstName,这里是通过整个对象的业务逻辑判断的,否则你需要在ui上面都定义一个验证规则,非常麻烦(比如有2个字段,你必须对两个字段都定义验证规则(即第一种方法)).这种做法是比较好的,把逻辑与ui分开了,值得推荐.sdk示例代码

  1. public class Person : IDataErrorInfo
  2.     {
  3.         private int age;
  4.         public int Age
  5.         {
  6.             get { return age; }
  7.             set { age = value; }
  8.         }
  9.         public string Error
  10.         {
  11.             get
  12.             {
  13.                 return null;
  14.             }
  15.         }
  16.         public string this[string name]
  17.         {
  18.             get
  19.             {
  20.                 string result = null;
  21.                 if (name == "Age")
  22.                 {
  23.                     if (this.age < 0 || this.age > 150)
  24.                     {
  25.                         result = "Age must not be less than 0 or greater than 150.";
  26.                     }
  27.                 }
  28.                 return result;
  29.             }
  30.         }
  31.     }

但还有些问题,如果我在第3点的基础上,我还要加一个普通的判断,比如必填验证,长度验证,还是逃不了验证规则这一步.
这里我们要做出选择
把规则定义在ui上(变动太大,不适合,无奈之举)
写在业务逻辑里面(这样下来,逻辑未免太复杂)

4.以元数据的形式(在属性上挂标签)

codeproject上,我找到一个比较完美的解决方案,作者自己重新定义了一套标签,使用也比较简单(代码是vb的,编译后再反编译用c#看:)),这种做法已经接近了要求.大家去看看这篇文章,非常的不错.其在还为前端提供了一个ui显示错误的一个下拉列表.不过通过这个例子,我又想到一个更好的东西.

5.使用EnterPrise Library Validation Application Block(再好不过)
由于wpf的属性使用了大量的依赖属性,我原以为这个好东西在wpf算是废了,通过第3点和第4点,我们终于可以引进这个企业级模块了。如下做法

  1.    public abstract class BaseValidationEntity<T> : IDataErrorInfo, INotifyPropertyChanged
  2.     {
  3.         IDataErrorInfo Members#region IDataErrorInfo Members
  4.         public string Error
  5.         {
  6.             get { return null; }
  7.         }
  8.         private T _entity;
  9.         public bool Valid()
  10.         {
  11.             Validator<T> validator = ValidationFactory.CreateValidator<T>();
  12.             ValidationResults results = validator.Validate(this);
  13.             return results.Count == 0;
  14.         }
  15.         public string this[string name]
  16.         {
  17.             get
  18.             {
  19.                 
  20.                 string result = null;
  21.                 
  22.                 Validator<T> validator = ValidationFactory.CreateValidator<T>();
  23.                 ValidationResults results = validator.Validate(this);
  24.                 if (results.Count > 0)
  25.                 {
  26.                     return results.First().Message;
  27.                     //foreach (var item in results)
  28.                     //{
  29.                     //    result += item.Message;
  30.                     //}
  31.                 }
  32.                 return result;
  33.             }
  34.         }
  35.         #endregion
  36.         protected void OnPropertyChanged(string name)
  37.         {
  38.             PropertyChangedEventHandler handler = PropertyChanged;
  39.             if (handler != null)
  40.             {
  41.                 handler(thisnew PropertyChangedEventArgs(name));
  42.             }
  43.         }
  44.         INotifyPropertyChanged Members#region INotifyPropertyChanged Members
  45.         public event PropertyChangedEventHandler PropertyChanged;
  46.         #endregion
  47.     }
然后继承之,随便定一个对象来demo

 

  1.   public class aa:BaseValidationEntity<aa>
  2.     {
  3.         private string firstName;
  4.         [StringLengthValidator(4, 10,MessageTemplate="aaa")]
  5.         [RegexValidator(@"/w+([-+.']/w+)*@/w+([-.]/w+)*/./w+([-.]/w+)*", MessageTemplate = "Invalid e-mail address")]
  6.         public string FirstName
  7.         {
  8.             get { return firstName; }
  9.             set { firstName = value;
  10.             OnPropertyChanged("FirstName");
  11.             }
  12.         }
  13.     }

我们看到熟悉的标签了,目前我认为这种方案最好,当然我们也可以通过配置xml来实现,这样真正做到了界面与逻辑分离。

上面一层的做到了逻辑上的验证,至于界面如何显示错误,我们可以通过Error属性来定制一个控件,这个暂不讨论了

在学习wpf的朋友,欢迎一起讨论。