WPF转换器之通用转换器
WPF中的转换器是一个非常好的数据类型转换解决方案,实用和强大, 它的作用是将源数据转换为WPF自身需要的类型,对数据实体没有侵略性,会在项目工程中频繁使用。所以掌握转换器是WPF开发的必备技能。
我刚接触转换器的时候,没有考虑通用性,每次遇到一个转换需求都会去创建一个新的转换器,久而久之,项目中的转换器已多如牛毛
这当然是我无法容忍的,我决定用一种通用转换器来代替绝大部分性质相同的转换操作,通过约定一套参数规则来适应不同的转换场景,达到转换器复用的目的。
转换器分为两种,IValueConverter(单值转换器)和IMultiValueConverter(多值转换器)
单值通用转换器 ObjectConverter
参数规则【比较值1|比较值2:true返回值:false返回值】
仔细一看,这个参数规则其实和三元表达式的含义相同,当源数据等于比较值1或者比较值2的时候,返回true返回值,否则返回false返回值
有了这个规则,就可以实现通用转换需求了,ObjectConverter源码如下
public class ObjectConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string[] parray = parameter.ToString().ToLower().Split(':'); //将参数字符分段 parray[0]为比较值,parray[1]为true返回值,parray[2]为false返回值 if (value == null) return parray[2]; //如果数据源为空,默认返回false返回值 if (parray[0].Contains("|")) //判断有多个比较值的情况 return parray[0].Split('|').Contains(value.ToString().ToLower()) ? parray[1] : parray[2]; //多值比较 return parray[0].Equals(value.ToString().ToLower()) ? parray[1] : parray[2]; //单值比较 } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { var returnValue = "otherValue"; string[] parray = parameter.ToString().ToLower().Split(':'); if (value == null) return returnValue; var valueStr = value.ToString().ToLower(); if (valueStr != parray[1]) return returnValue; else return parray[0].Contains('|') ? parray[0].Split('|')[0] : parray[0]; } }
ObjectConverter的使用
1 <converter:ObjectConverter x:Key="objConverter"/> 2 3 <Border Visibility="{Binding PanelStatus, Converter={StaticResource objConverter},ConverterParameter=true:Visible:Collapsed}"> 4 5 <Border Background="{Binding BgColor, Converter={StaticResource objConverter},ConverterParameter=1:Red:Blue}"> 6 7 <TextBlock Text="{Binding Type, Converter={StaticResource objConverter},ConverterParameter=1|2:VIP会员:普通会员}"/>
多值通用转换器 MultiObjectConverter
参数规则【各组比较值:比较条件(&或|):true返回值:false返回值:返回值类型枚举】
多值转换器的参数规则略显麻烦,但是也和三元表达式类似,只不过多了比较条件和返回值枚举类型,这里有人要问了为什么单值转换器不需要声明返回值枚举而多值转换器需要,这是因为多值转换器的返回值不返回实际类型的话,返回类型会失效,WPF的默认转换器似乎没有起到作用。这个问题我也正在研究,所以先定义一个返回值枚举用来转换返回值的类型。姑且算一个临时解决方案吧
1 public class MultiObjectConverter : IMultiValueConverter 2 { 3 /// <summary> 4 /// 多值转换器 5 /// </summary> 6 /// <param name="values">参数值数组</param> 7 /// <param name="parameter"> 8 /// <para>参数</para> 9 /// <para>各组比较值:比较条件(&或|):true返回值:false返回值:返回值类型枚举</para> 10 /// <para>v1;v2-1|v2-2;v3:&:Visible:Collapsed:1</para> 11 /// </param> 12 /// <returns></returns> 13 public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 14 { 15 string[] param = parameter.ToString().ToLower().Split(':'); //将参数字符串分段 16 string[] compareValues = param[0].Split(';'); //将比较值段分割为数组 17 if (values.Length != compareValues.Length) //比较源数据和比较参数个数是否一致 18 return ConvertValue(param[3], param[4]); 19 var trueCount = 0; //满足条件的结果数量 20 var currentValue = string.Empty; 21 IList<string> currentParamArray = null; 22 for (var i = 0; i < values.Length; i++) 23 { 24 currentValue = values[i] != null ? values[i].ToString().ToLower() : string.Empty; 25 if (compareValues[i].Contains("|")) 26 { 27 //当前比较值段包含多个比较值 28 currentParamArray = compareValues[i].Split('|'); 29 trueCount += currentParamArray.Contains(currentValue) ? 1 : 0; //满足条件,结果+1 30 } 31 else 32 { 33 trueCount += compareValues[i].Equals(currentValue) ? 1 : 0; //满足条件,结果+1 34 } 35 } 36 currentParamArray = null; 37 currentValue = string.Empty; 38 var compareResult = param[1].Equals("&") ? 39 trueCount == values.Length : 40 trueCount > 0; //判断比较结果 41 return ConvertValue(compareResult ? param[2] : param[3], param[4]); 42 } 43 44 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 45 { 46 throw new NotImplementedException(); 47 } 48 49 private object ConvertValue(string result, string enumStr) 50 { 51 var convertResult = (ConvertResult)int.Parse(enumStr); 52 if (convertResult == ConvertResult.显示类型) 53 return result.Equals("collapsed") ? Visibility.Collapsed : Visibility.Visible; 54 if (convertResult == ConvertResult.布尔类型) 55 return System.Convert.ToBoolean(result); 56 return null; //后续自行扩展 57 } 58 59 private enum ConvertResult 60 { 61 显示类型 = 1, 62 布尔类型 = 2, 63 字符串类型 = 3, 64 整型 = 4, 65 小数型 = 5, 66 画刷类型 = 6, 67 样式类型 = 7, 68 模板类型 = 8 69 } 70 }
MulitObjectConverter的使用
1 <TextBlock Text="test"> 2 <TextBlock.Visibility> 3 <MultiBinding Converter="{StaticResource mobjConverter}" 4 ConverterParameter="1|2;true:|:Visible:Collapsed:1"> 5 <Binding Path="Filed1"/> 6 <Binding Path="Filed2"/> 7 </MultiBinding> 8 </TextBlock.Visibility> 9 </TextBlock>
单值通用转换器和多值通用转换器已经讲完了,读者可以根据自己的需求定制ConverterParameter的规则,实现灵活扩展。欢迎入群交流372754241