上一篇讲了复合控件的基础知识,本来接着要继续讲复合控件样式的使用,让我们暂时回到前面第五篇的时候,继续讨论关于属性方面的一些知识.写第五篇的时候,我一步步的加上元数据(特性),使得设计时效果更加好,如对复杂属性应用以下特性,使属性浏览器支持扩展/折叠效果,使你更加容易编辑子属性,但接着我又遇到了问题,所以必须去解决1.认识默认属性浏览器支持让我们再认识一下属性,大家知道每个属性都是有类型的,最熟悉就是string,int这些类型了,vs2005属性浏览器对这些属性类型进行了识别,如下例子(1)table控件的Height属性,当你设置属性为字符串时,则提示错误信息(2)当属性类型为Color属性时,属性浏览器为你提供颜色选择器(3)当属性类型为枚举类型时,属性浏览器则支持下拉框选择(4)当类型是时间类型,属性浏览器则支持时间选择器通过上面,我们认识到属性浏览器默认会判别属性类型,当属性值跟属性类型不符时,则会提示错误信息.这里我们还认识到属性浏览器默认为一些属性类型提供了便利2.属性表现形式的多样性在定义控件属性时,可以直接这样定义,属性都为字符串形式
< asp:TextBox ID ="TextBox1" runat ="server" Height ="11" BackColor ="Blue" ForeColor ="#FF8000" > 测试 </ asp:TextBox >用代码表示则是这样,在后台代码中定义的属性类型必须相对应,BackColor必须为Color类型,否则则会出错,当在页面呈现时,则以字符串形式呈现.
protected void Page_Load( object sender, EventArgs e) { //TextBox1.BackColor = "blue"; TextBox1.BackColor = System.Drawing.Color.Red; TextBox1.BackColor = System.Drawing.Color.FromName("blue"); } 通过上面,我们认识到属性类型需要转换,这里便要引出我们所要讲的话题,类型转换器. 例如,当BackColor="Blue" 时,则会激活一个类型转换器实例将字符串值转换成声明的类型(即将"blue"转换成Color类型,然后赋给BackColor..net类库中的基本类型和许多类型都有与其相关联的类型转换器.一般常用的类型有String,Int,Boolean,DateTime,Enum等类型,其类型已默认与其相对应的类型转换器关联起来.如Color类默认关联的类型转换器System.Drawing.ColorConverterFontInto类默认关联的类型转换器System.Drawing.FontConverter类型转换器的基类为System.ComponentModel.TypeConverter,所有的类型转换器都从其派生. 下面我们再来看一个例子, 我们先定义一个复杂属性,用于测试 示例一 1using System; 2using System.Collections.Generic; 3using System.Text; 4 5namespace CustomComponents 6{ 7 public class Name 8 { 9 private string firstName;10 private string lastName;11 public String FirstName12 {13 get14 {15 return firstName;16 }17 set18 {19 firstName = value;20 }21 }2223 public String LastName24 {25 get26 {27 return lastName;28 }29 set30 {31 lastName = value;32 }33 }34 }35} 再看aspx文件 其中我们输出了Label的几个属性,包括FontInfo这个复杂属性,并且实例化了上面定义的Name类 1<%@ Page Language="C#" %> 2 3<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 4 5<script runat="server"> 6 7 void Page_Load(object sender, EventArgs e) 8 { 9 myLabel.Font.Bold = true;10 myLabel.Font.Italic = false;11 myLabel.Font.Name = "verdana";12 myLabel.Font.Overline = false;13 myLabel.Font.Size = 10;14 myLabel.Font.Strikeout = false;15 myLabel.Font.Underline = true;16 17 Response.Write(myLabel.Font + "<br>");18 Response.Write(Label1.Width);19 Response.Write(Label1.BackColor+"<br>");20 Response.Write(Label1.ForeColor + "<br>");2122 CustomComponents.Name n = new CustomComponents.Name();23 n.FirstName = "张";24 n.LastName = "三";25 Response.Write(n);26 }2728</script>29<html xmlns="http://www.w3.org/1999/xhtml" >30<head id="Head1" runat="server">31 <title>FontInfo Example</title>32</head>33 <body>34 <form id="form1" runat="server">35 <h3>FontInfo Example</h3>36 <asp:Label id="myLabel" 37 runat="server" >38 </asp:Label>39 <br />40 <br />41 <asp:Label ID="Label1" Width="200px" runat="server" BackColor="#FF8080" Text="Label" ForeColor="Red"></asp:Label>42 </form>43 </body>44</html>4546 下面看看输出结果,如下图 结果是将FontInfo属性和Color属性转换成字符串形式呈现,而我们自定义的Name属性只以其类型呈现,并未呈现其子属性值. 再来看看第一点 默认属性浏览器支持和第五篇我们所定义的一个复杂属性CustomComponents.Address, CustomComponents.Address属性同样存在着这样的问题,再对比一下FontInfo属性在 属性浏览器的支持,如下图 --------------------------------------------------------------------------- 复杂属性CustomComponents.Address无法编辑,只读,而且显示的是其属性类型. 复杂属性Font则默认显示了子其属性的值(其默认只显示Names值和Size值,且为只读) 问题来了, 我们要根据需要为自定义属性实现类型转换器. 3.自定义属性类型转换器上面已经说明问题所在了,实现类型转换器,可以将属性类型和字符串类型之间相互转换. 下面我们就为解决这个问题而来了解类型转换器,我们还是以 第五篇的那个例子来学习 在说第二点 属性表现形式的多样性的时候已经说过了 类型转换器的基类为 System.ComponentModel.TypeConverter,所有的类型转换器都从其派生. 下面以第五篇时的例子为基础 我们为Address实现了一个类型转换器,其实现了复杂属性代码的折叠,如下代码 [TypeConverter( typeof (ExpandableObjectConverter))] public class Address {} System.ComponentModel.ExpandableObjectConverter 类也是从TypeConverter类派生 所以当自定义复杂类型的类型转换器时,则可从ExpandableObjectConverter 类派生 如果你定义的属性不是复杂属性,则可以直接从TypeConverter类派生 (1) 值翻译的类型转换器 从TypeConverter类派生的自定义类型转换器则需要重写TypeConverter类几个方法,主要目的就是实现自定义属性类型与字符串值之间的转换. 你需要了解以下几个方法,这里我们就以Address属性为例子,这样来解释以下方法更好理解. 其实可以理解成一来一去的关系,Form,To,相互之间的转换,如下代码: TypeDescriptor类的方法为静态方法,通过GetConverter方法获取类型转换器 然后通过TypeConverter类的ConvertFromString方法和ConvertToString相互转换类型. 示例二 public class AddressConverter : ExpandableObjectConverter { 方法#region 方法 // 返回值能否将String类型转换为Address类型 //sourceType表示要转换的类型 public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) { return true; } return base.CanConvertFrom(context, sourceType); } // 返回值能否将Address类型转换为String类型 //sourceType表示要转换到的类型 public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string)) { return true; } return base.CanConvertTo(context, destinationType); } //将String类型转换为Address类型 //value为要转换的类型 public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value == null) { return new Address(); } if (value is string) { string s = (string)value; if (s.Length == 0) { return new Address(); } string[] parts = s.Split(culture.TextInfo.ListSeparator[0]); if (parts.Length != 4) { throw new ArgumentException("Invalid Address", "value"); } //返回指定类型转换器 TypeConverter stringConverter = TypeDescriptor.GetConverter(typeof(string)); return new Address((string)stringConverter.ConvertFromString(context, culture, parts[0]), (string)stringConverter.ConvertFromString(context, culture, parts[1]), (string)stringConverter.ConvertFromString(context, culture, parts[2]), (string)stringConverter.ConvertFromString(context, culture, parts[3])); } return base.ConvertFrom(context, culture, value); } //将Address类型转换为String类型 //value为要转换的类型 public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value != null) { if (!(value is Address)) { throw new ArgumentException( "Invalid Address", "value"); } } if (destinationType == typeof(string)) { if (value == null) { return String.Empty; } Address ad = (Address)value; TypeConverter stringConverter = TypeDescriptor.GetConverter(typeof(string)); return String.Join(culture.TextInfo.ListSeparator, new string[] { stringConverter.ConvertToString(context, culture, ad.Street), stringConverter.ConvertToString(context, culture, ad.City), stringConverter.ConvertToString(context, culture, ad.State), stringConverter.ConvertToString(context, culture, ad.Zip) }); } return base.ConvertTo(context, culture, value, destinationType); } #endregion }自定义好类型转换器后,要与属性相关联起来
[TypeConverter( typeof (AddressConverter))] public class Address { public Address() : this(String.Empty, String.Empty, String.Empty, String.Empty) { } public Address(string street, string city, string state, string zip) { this.street = street; this.city = city; this.state = state; this.zip = zip; } ....... }下面来看下效果,如下图,属性编辑器显示的CustomAddress不再显示其类型了,已将其转化为字符串形式了,如果你设置CustomAddress可写的话,就可以直接编辑CustomAddress属性,但其只有四个子属性,所以当大于5个时就会出错,且格式也不可以乱改,一般情况下都设置其只读.为了在页面上显示这个CustomAddress属性,还需要为Address类重写一下ToString方法,如下代码
方法 #region 方法 public override string ToString() { return ToString(CultureInfo.CurrentCulture); } public virtual string ToString(CultureInfo culture) { return TypeDescriptor.GetConverter(typeof(Address)).ConvertToString(null, culture, this); } #endregion通过上面重写ToString方法后,输出的CustomAddress属性将不再是其类型,而是以字符串形式呈现,以下以Response.Write方法输出Custom1.CustomAddress输出,如下图上面讲了最基本的值翻译类型转换器,希望对大家有帮助(2)向“属性”窗口提供标准值列表的类型转换器像省份这样的属性,为了方便用户填写,我们往往做成下拉框形式,一个省份里面又有城市,我们往往列出一部分,如果其中数据不符合用户要求的话,用户还可以自己输入,使用类型转换器转换器也可以做到这一点.实现这一效果你需要重写以下方法,我们添加一个属性喜欢的游戏的名称因为属性为String类型,可以直接从StringConverter 派生示例三
public class GameConverter : StringConverter { //返回此对象是否支持可以从列表中选取的标准值集 public override bool GetStandardValuesSupported( ITypeDescriptorContext context) { return true; } //返回下拉框集合类 public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { return new StandardValuesCollection(new string[]{"传奇", "魔兽世界", "龙与地下城"}); } //标准值的集合是否为独占列表 //默认为flase,为true则表示无法修改列表值 public override bool GetStandardValuesExclusive( ITypeDescriptorContext context) { return false; } }然后与相关属性关联起来
[TypeConverter( typeof (GameConverter))] [Description( " 喜欢的游戏 " )] public String Game { get { return game; } set { game = value; } }好了,.下面我们看一下效果,如下图,你可以选择下拉框的值,也可以自己手动输入,跟枚举类型很相似,但枚举类型无法自己修改值.这一篇主要介绍了类型转换器的基本使用,希望对大家有所帮助,在写的同时我也学到了很多,讲的比较基础,什么时候我懂了可以再补充,水平有限呀.这一篇就写到这里,很高兴,我已经写到第9篇了,我会继续写下去的,也希望大家喜欢.
