寫第五篇的時候,我一步步的加上元數據(特性),使得設計時效果更加好,如對複雜屬性應用以下特性,使屬性瀏覽器支持擴展/摺疊效果,使你更加容易編輯子屬性,但接着我又遇到了問題,所以必須去解決
1.認識默認屬性瀏覽器支持
讓我們再認識一下屬性,大家知道每個屬性都是有類型的,最熟悉就是string,int這些類型了,vs2005屬性瀏覽器對這些屬性類型進行了識別,
如下例子
(1)table控件的Height屬性,當你設置屬性爲字符串時,則提示錯誤信息
(2)當屬性類型爲Color屬性時,屬性瀏覽器爲你提供顏色選擇器
(3)當屬性類型爲枚舉類型時,屬性瀏覽器則支持下拉框選擇
(4)當類型是時間類型,屬性瀏覽器則支持時間選擇器
通過上面,我們認識到屬性瀏覽器默認會判別屬性類型,當屬性值跟屬性類型不符時,則會提示錯誤信息.這裏我們還認識到屬性瀏覽器默認爲一些屬性類型提供了便利
2.屬性表現形式的多樣性
在定義控件屬性時,可以直接這樣定義,屬性都爲字符串形式
Height="11" BackColor="Blue"
ForeColor="#FF8000">測試</asp:TextBox>
用代碼表示則是這樣,在後臺代碼中定義的屬性類型必須相對應,BackColor必須爲Color類型,否則則會出錯,當在頁面呈現時,則以字符串形式呈現.
{
//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.ColorConverter
FontInto類默認關聯的類型轉換器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 FirstName
12 {
13 get
14 {
15 return firstName;
16 }
17 set
18 {
19 firstName = value;
20 }
21 }
22
23 public String LastName
24 {
25 get
26 {
27 return lastName;
28 }
29 set
30 {
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>");
21
22 CustomComponents.Name n = new CustomComponents.Name();
23 n.FirstName = "張";
24 n.LastName = "三";
25 Response.Write(n);
26 }
27
28</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>
45
46
下面看看輸出結果,如下圖
結果是將FontInfo屬性和Color屬性轉換成字符串形式呈現,而我們自定義的Name屬性只以其類型呈現,並未呈現其子屬性值.
再來看看第一點默認屬性瀏覽器支持和第五篇我們所定義的一個複雜屬性CustomComponents.Address,
CustomComponents.Address屬性同樣存在着這樣的問題,再對比一下FontInfo屬性在屬性瀏覽器的支持,如下圖
---------------------------------------------------------------------------
複雜屬性CustomComponents.Address無法編輯,只讀,而且顯示的是其屬性類型.
複雜屬性Font則默認顯示了子其屬性的值(其默認只顯示Names值和Size值,且爲只讀)
問題來了,我們要根據需要爲自定義屬性實現類型轉換器.
3.自定義屬性類型轉換器
上面已經說明問題所在了,實現類型轉換器,可以將屬性類型和字符串類型之間相互轉換.
下面我們就爲解決這個問題而來了解類型轉換器,我們還是以第五篇的那個例子來學習
在說第二點屬性表現形式的多樣性的時候已經說過了
類型轉換器的基類爲System.ComponentModel.TypeConverter,所有的類型轉換器都從其派生.
下面以第五篇時的例子爲基礎
我們爲Address實現了一個類型轉換器,其實現了複雜屬性代碼的摺疊,如下代碼
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
}
自定義好類型轉換器後,要與屬性相關聯起來
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方法,如下代碼
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 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;
}
}
然後與相關屬性關聯起來
[Description("喜歡的遊戲")]
public String Game
{
get
{
return game;
}
set
{
game = value;
}
}
好了,.下面我們看一下效果,如下圖,你可以選擇下拉框的值,也可以自己手動輸入,跟枚舉類型很相似,但枚舉類型無法自己修改值.
這一篇主要介紹了類型轉換器的基本使用,希望對大家有所幫助