[WPF]Binding的Converter和Validator

系统 2170 0

不拘一格用数据的Converter

上篇文已经说明,Binding就是数据源与目标之间的“关联”。大多数情况下,数据从Source到Target以及从Target返回Source都是“直来直去”的,但有些场景却需要我们对数据做些转换才能为我所用。举两个典型的例子:

  • 如果数据源里的值是Y和N,如果是Y,那么UI上的CheckBox就被勾选,否则就不勾选,这就需要我们把string(也许是char)类型的数据转换成bool?类型再使用。如果Binding是TwoWay的,CheckBox的勾选操作还会把值传回数据源。
  • 如果“评论内容”TextBox里没有内容,则“提交”Button不可以点击。这是个典型的OneWay数据Binding,因为只有TextBox去影响Button的份儿。具体如何实现,大家可以先猜猜;)

想要实现这类的转换,就需要为Binding这个“绿色通道”设置“关卡”,这里我们用到的关卡就是“数据转换器”(Data Converter)。Converter实际上就是一个类,它这个类有个要求——它需要实现 IValueConverter 这个接口。这个接口的内容非常简单——只有两个方法,它们分别是:

  • Convert方法 :按照你的要求,把从数据源传来的数据转成你想要的数据——至于是加减乘除还是煎炒炸炖,那就要看你怎么实现函数体了
  • ConvertBack 方法:如果Binding是TwoWay的,那么数据目标会回传经用户改动后的数据,这时候你就不得不把数据转换回数据源里的格式——大多数情况下,它是Convert方法的逆运算,具体情况还要具体分析。(不过,熟饭估计怎么着也变不成生米了,呵呵~~)

 下面是第一个例子的核心代码,我来一步一步实现。

第一步 :先声明一个类。我的习惯是 用Converter开头,后缀是“源类型2目标类型” ,这里的“2”是“to”的意思。

 

[csharp]   view plain copy
 
  1. class  ConverterYN2TF  
  2. {  
  3.   
  4. }  

第二步 :让这个类实现 IValueConverter 接口。这里有个使用VS2008的小窍门——在类名后写上“: IValueConverter ”后,按下键盘上的“Shift+Alt+F10”会弹出VS2008的智能菜单,选择其中的第一项“实现IValueConverter的方法”,VS2008会自动为我们生成需要实现的方法体:

[csharp]   view plain copy
 
  1. class  ConverterYN2TF : IValueConverter  
  2. {  
  3.     #region IValueConverter Members   
  4.   
  5.      public   object  Convert( object  value, Type targetType,  object  parameter, System.Globalization.CultureInfo culture)  
  6.     {  
  7.          throw   new  NotImplementedException();  
  8.     }  
  9.   
  10.      public   object  ConvertBack( object  value, Type targetType,  object  parameter, System.Globalization.CultureInfo culture)  
  11.     {  
  12.          throw   new  NotImplementedException();  
  13.     }  
  14.  
  15.     #endregion   
  16. }  

第三步 :添加Attribute。这步不是必需的,但加上有是有好处的——告诉Converter数据的源类型与目标类型各是什么。值得注意的是,CheckBox的IsChecked属性是bool?类型的(可空bool类型),意思是说可以是True/False/Null三种值,表现在UI上就是勾选/不勾选/中间态。如果想让CheckBox能显示中间态,需要把它的IsThreeState属性设为True。

[csharp]   view plain copy
 
  1. [ValueConversion( typeof ( string ),  typeof ( bool ?))]  //数据的源类型是string,目标类型是bool?   
  2. class  ConverterYN2TF : IValueConverter  
  3. {  
  4.  
  5.     #region IValueConverter Members   
  6.   
  7.      public   object  Convert( object  value, Type targetType,  object  parameter, System.Globalization.CultureInfo culture)  
  8.     {  
  9.          throw   new  NotImplementedException();  
  10.     }  
  11.   
  12.      public   object  ConvertBack( object  value, Type targetType,  object  parameter, System.Globalization.CultureInfo culture)  
  13.     {  
  14.          throw   new  NotImplementedException();  
  15.     }  
  16.  
  17.     #endregion   
  18. }  

第四步:实现这两个方法。在开始动手前,我们先分析一下这两个方法的参数和返回值。

首先,这两个方法的参数和返回值都是object类型的,之所以这样做,是因为接口的设计者并不知道你要传入和传出的数据是什么类型的,只好用它们“绝对正确”的基类——object了。

其次,对于Convert方法来说, value是从数据源传来的数据,返回值是转换好后发送给数据目标的数据。对于ConvertBack方法正好反过来,value是从数据目标(比如UI)传回来的数据,返回值是要与数据源匹配的数据。

再次,偶尔我们会用到parameter那个参数。比如在转换某些数据的时候,我们需要依赖一些其它的外部数据来辅助我们的数据转换,这时候就可以在parameter上打主意了。如果想传多个参数的话,可以把这些参数打包成数组或者class/struct等数据结构再传进来。在我们工作的代码中用到过一次parameter,我为我的Converter类准备了一个带参数的构造函数,把外部的辅助数据传给Converter

最后,如果你的Binding是OneWay的,那么恭喜你——你的ConvertBack函数体随便怎么实现都可以——因为它不可能被调用。

完成的类是这样的:

[csharp]   view plain copy
 
  1. [ValueConversion( typeof ( string ),  typeof ( bool ?))]  //数据的源类型是string,目标类型是bool?   
  2. class  ConverterYN2TF : IValueConverter  
  3. {  
  4.      public   object  Convert( object  value, Type targetType,  object  parameter, System.Globalization.CultureInfo culture)  
  5.     {  
  6.          string  str = System.Convert.ToString(value);  
  7.          switch  (str)  
  8.         {  
  9.              case   "Y" :  
  10.                  return   true ;  
  11.              case   "N" :  
  12.                  return   false ;  
  13.              default :  
  14.                  return   null ;  
  15.         }  
  16.     }  
  17.   
  18.      public   object  ConvertBack( object  value, Type targetType,  object  parameter, System.Globalization.CultureInfo culture)  
  19.     {  
  20.          bool ? b = System.Convert.ToBoolean(value);  
  21.          switch  (b)  
  22.         {  
  23.              case   true :  
  24.                  return   "Y" ;  
  25.              case   false :  
  26.                  return   "N" ;  
  27.              default :  
  28.                  return   "Null" ;  
  29.         }  
  30.     }  
  31. }  

使用这个类的方法是将Binding实例的Converter属性设置为这个类的一个实例:

[csharp]   view plain copy
 
  1. checkBox1.IsThreeState =  true ;  
  2. Binding binding =  new  Binding( "Text" );  
  3. binding.Source = textBox1;  
  4. binding.Converter =  new  ConverterYN2TF();  // 设定Converter   
  5. this .checkBox1.SetBinding(CheckBox.IsCheckedProperty, binding);  

 

至于上面的第二个例子,留给大家自己动手去实现吧。想一想:怎样才能让Button的IsEnable属性与TextBox中文本的有无关联上呢? 

  让数据“干干净净”的Validation

再让我们来看看如何对数据进行“安检”。 

首先,这里有一个“霸王条款”——Binding认为从数据源出去的数据都是“干净”的,所以不进行校验;只有从数据目标回传的数据才有可能是“脏”的,需要校验。

其次,对于一个Binding而言,Converter只能有一个,而校验条件可以是好几个——它们存储在Binding的ValidationRules这个集合里。其实,数据校验与转换做的事儿差不多。

下面给出一个例子:我们以一个Slider为数据源,它的滑块可以从Value=0滑到Value=100;同时,我们以一个TextBox为数据目标,并通过Validation限制它只能将20到35之间的数据传回数据源。现实当中恐怕很少有这么干的,我们这个例子只是为了说明校验的使用方法:)

若要创建一个自定义的校验条件,需要声明一个类,并让这个类派生自ValidationRule类。ValidationRule只有一个名为Validate的方法需要我们实现,这个方法的返回值是一个ValidationResult类型的实例——这个实例携带着两个信息:

  • bool类型的 IsValid属性告诉Binding回传的数据是否合法
  • object类型(一般是存储一个string)的ErrorContent属性告诉Binding一些信息,比如当前是进行什么操作而出现的校验错误等等,一般我会把这些信息写进Log文件里

实现好的类是这样的:

[csharp]   view plain copy
 
  1. public   class  MyValidationRule : ValidationRule  
  2. {  
  3.      public   override  ValidationResult Validate( object  value, System.Globalization.CultureInfo cultureInfo)  
  4.     {  
  5.          double  d = 0.0;  
  6.          if  ( double .TryParse(( string )value,  out  d) && d >= 20 && d <= 35)  
  7.         {  
  8.              return   new  ValidationResult( true "OK" );  
  9.         }  
  10.          else   
  11.         {  
  12.              return   new  ValidationResult( false "Error" );  
  13.         }  
  14.     }  
  15. }  

在代码里这样使用它:

[csharp]   view plain copy
 
  1. Binding binding =  new  Binding( "Value" );  
  2. binding.Source = slider1;  
  3. binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;  
  4. binding.ValidationRules.Add( new  MyValidationRule());  // 加载校验条件   
  5. textBox1.SetBinding(TextBox.TextProperty, binding);  

[WPF]Binding的Converter和Validator


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论