zoukankan      html  css  js  c++  java
  • 【转载】wpf学习笔记2

     

    http://blog.csdn.net/fantasiax/article/details/4575968

    深入浅出WPF(8)——数据的绿色通道,Binding(中)

    小序:

    今天中午吃完饭回工位的路上,和俺们组资深的Level 2技术支持肖老师聊了几句。我跟肖老师说,最近我在学习Binding,肖老师说——那可不是个好东西!因为如果在程序中使用了Binding,当出现错误的时候,比较难于调试。道理很简单——以前使用事件(C++里是回调)的时候,能明确地在事件处理函数里去跟踪调试,现在使用Binding,数据源和UI之间是一支封闭的“管道”,在代码中很难看到他们是在哪里关联上的、出了问题也不知道在哪里拦截、设断点。

    这的确是个问题!我又去请教了别的同事。Allen同学告诉我,在Binding和数据目标(的Dependency Property)有一些事件,会在数据有传输的时候被激发,或者在Binding的Converter和Validator上下下功夫。看来Binding值得研究的地方还真多呀!

    文章的篇幅毕竟有限,我只能捡工作当中用的最多的来介绍。那么,我还有哪些东西需要介绍呢?

    • 让数据“为我所用”的Converter
    • 让数据“干干净净”的Validation
    • 集合控件与集合数据的Binding
    • 偷懒专用的数据中转站——DataContext

    希望我的工作能给大家搭起一个良好的学习框架——全面细致的内容尽在MSDN里,请大家阅读的时候会轻松一些。

    正文

    不拘一格用数据的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”的意思。

    1. class ConverterYN2TF 
    2.  

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

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

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

    1. [ValueConversion(typeof(string), typeof(bool?))] //数据的源类型是string,目标类型是bool? 
    2. class ConverterYN2TF : IValueConverter 
    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.     #endregion 

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

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

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

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

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

    完成的类是这样的:

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

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

    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文件里

    实现好的类是这样的:

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

    在代码里这样使用它:

    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); 

    程序执行起来之后的效果是这样的:

    我可以使用Slider滑出从0到100的值来,也可以使用TextBox输入20到35之间的值;但当我输入小于20或者大于35的数字以及非数字时,值就不会被传回到Slider(数据源),同时,TextBox还会被一个红色的边框圈起来以示警告。至于警告的风格,我们可以自定义,具体怎么定义我会放在Style一节去叨叨。

    TO BE CONTINUE...

    下篇文中我们将介绍非常实用的集合Binding与懒人的最爱——DataContext

    小知识:什么是L2

    CA有些同事(而且是C++比较牛的同事)是做Level 2的,也就是“二级技术支持”,Level 1是接客户电话、对客户进行支持的——他们最重要的工作就是当用户发现bug时用开心的口吻告诉他们:“Hi,that a feature:)”,呵呵,开个玩笑,实际上他们经常面对怒气冲冲、快要发疯的客户。如果真的是个bug,那L1的同学们也搞不定,这时候就要把bug提交给L2的同学们了,L2的同学们会连夜改代码、解bug、出SP包……算是我们的“救火队员”了,公司的良好信誉离不开他们的努力。

  • 相关阅读:
    训练总结
    图论--最短路--SPFA模板(能过题,真没错的模板)
    图论--最短路-- Dijkstra模板(目前见到的最好用的)
    The 2019 Asia Nanchang First Round Online Programming Contest B Fire-Fighting Hero(阅读理解)
    关于RMQ问题的四种解法
    The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 K题 center
    The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 XKC's basketball team
    The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 D Carneginon
    ZOJ 3607 Lazier Salesgirl (枚举)
    ZOJ 3605 Find the Marble(dp)
  • 原文地址:https://www.cnblogs.com/fx2008/p/2423457.html
Copyright © 2011-2022 走看看