1.錯誤的代碼,無法解析
首先來看一段簡單的代碼
正確
<asp:Wizard ID="Wizard1" runat="server">
<WizardSteps>
<asp:WizardStep ID="WizardStep1" runat="server" Title="Step 1">
21212</asp:WizardStep>
<asp:WizardStep ID="WizardStep2" runat="server" Title="Step 2">
</asp:WizardStep>
</WizardSteps>
</asp:Wizard>
錯誤
<asp:Wizard ID="Wizard2" runat="server">
<asp:WizardStep ID="WizardStep1" runat="server" Title="Step 1">
21212</asp:WizardStep>
<asp:WizardStep ID="WizardStep2" runat="server" Title="Step 2">
</asp:WizardStep>
</asp:Wizard>
<br />
<br />
<asp:Label ID="Label1" runat="server" Text="Label">
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
</asp:Label>
<br />
<asp:TextBox ID="TextBox3" runat="server">12345</asp:TextBox>
<br />
<asp:Label ID="Label2" runat="server" Text="Label">12345</asp:Label>
<br />
<br />
<asp:TextBox ID="TextBox1" runat="server">
<asp:Label runat="server" Text="Label"></asp:Label>
</asp:TextBox>
Wizard為asp.net2.0新增的一個控件,這個頁面發生兩個錯誤,如下圖

運行此頁面后則會報錯,出現以下提示
分析器錯誤
這里有一個問題,.net提供我們控件時,我們已經形成一種定向思維,控件就是那樣寫的,如DropDownList,其中只能是套<asp:ListItem>的,那我為什么就不能套別的屬性呢?Wizard控件為什么就要加一個WizardSteps屬性才可以正常運行呢?當我們思考到這里,我們就該尋找答案.
2.從ParseChildren元數據屬性講起
從第五篇開始,我們多次用到了ParseChildren此特性.還請大家先看MSDN對其的解釋,其有三種情況
(1)ParseChildren(true) 第5篇我們使用集合屬性的時候我們曾經這樣定義,如下代碼
[ParseChildren(true)]
public class Custom: Control

{
}
(2)ParseChildren(true,"<Default Property>") 第10篇當我們定義集合屬性時,我們曾這樣定義
DropItemList為集合屬性
[ParseChildren(true, "DropItemList")]
public class DropColor:WebControl

{
}
(3)ParseChildren(false) 這個我們沒用過,也是我們要講的內容,當其內部定義為flase時,那么放在此控件內的元素將被解析成控件,應該說是頁分析器ControlBuilder 類.這里大家可以看看MSDN文檔對ControlBuilder 類的解釋,至少要先知道這一點。默認情況下,頁上的每個控件都與一個默認的 ControlBuilder 類關聯。
下面我們慢慢看下來.
3.控件與集合屬性
讓我們再次回憶一下ParseChildren的用法,本次的示例代碼取自asp.net2.0揭密
(1) ParseChildren(true,"<Default Property>")的使用,此控件實現效果為隨機顯示一個內部控件內容 。RItem為一個繼承Control的控件,其內部未實現任何東西,你可以在其控件內部輸出呈現,記得上面說的ControlBuilder 類默認關聯
示例一
[ParseChildren(true, "Items")]
public class ItemRotator : CompositeControl

{
private ArrayList _items = new ArrayList();

[Browsable(false)]
public ArrayList Items

{

get
{ return _items; }
}

protected override void CreateChildControls()

{
Random rnd = new Random();
int index = rnd.Next(_items.Count);
Control item = (Control)_items[index];
this.Controls.Add(item);
}
}

public class RItem : Control

{

}
頁面代碼
<custom:ItemRotator
id="ItemRotator1"
Runat="server">
<custom:ritem ID="Item1" runat="server">
First Item
</custom:ritem>
<custom:ritem ID="Item2" runat="server">
Second Item
<asp:Calendar
id="Calendar1"
Runat="server" />
</custom:ritem>
<custom:ritem ID="Item3" runat="server">
Third Item
</custom:ritem>
</custom:ItemRotator>
效果就不說了,隨機顯示ritem控件的內容,注意以上控件定義了一個Items集合屬性,另外改進的話就是我們第十篇的講的,為Ritem定義屬性,作為一個集合屬性,這里就不再列出代碼.
(1)ParseChildren(false)的使用
此控件未添加屬性,而多了一個方法AddParsedSubObject(),控件有默認的頁面分析邏輯,重寫AddParsedSubObject方法,可以向控件添加子控件
示例二
[ParseChildren(false)]
public class ContentRotator : WebControl

{

protected override void AddParsedSubObject(object obj)

{
if (obj is Content)
base.AddParsedSubObject(obj);
}

protected override void RenderContents(HtmlTextWriter writer)

{
Random rnd = new Random();
int index = rnd.Next(this.Controls.Count);
this.Controls[index].RenderControl(writer);
}
}
[
ToolboxItem(false)
]
public class Content : Control

{
}
頁面代碼
<custom:ContentRotator
id="ContentRotator1"
Runat="server">
<custom:Content
id="Content1"
Runat="server">
顯示的第一項,此不為屬性
</custom:Content>
<custom:Content
id="Content2"
Runat="server">
顯示的第二項,此不為屬性
<asp:Calendar
id="Calendar1"
Runat="server" />
</custom:Content>
<custom:Content
id="Content3"
Runat="server">
顯示的第三項,此不為屬性
</custom:Content>
</custom:ContentRotator>
注意:ContentRotator無任何屬性(其內部添加的為控件),而是用AddParsedSubObject 方法向控件添加了子控件,而不像ItemRotator控件一樣,其內部是屬性而非控件.
4.修改默認解析邏輯
上面已經說過每個控件都有默認的解析邏輯,其通過ControlBuilder 類來實現,可以通過重寫其方法來自定義解析邏輯.下面通過一個例子來說明,它把一個控件以自定義標簽所代替,以下列出部分代碼
示例三
//自定義頁分析器
public class ServerTabsBuilder : ControlBuilder

{
public override Type GetChildControlType(string tagName, IDictionary attribs)

{
if (String.Compare(tagName, "tab", true) == 0)
return typeof(ServerTab);
else
return null;
}
}

[ToolboxItem(false)]
public class ServerTab : Control

{
private string _Text;

public string Text

{

get
{ return _Text; }

set
{ _Text = value; }
}
}
(1)ServerTabsBuilder類重寫了
ControlBuilder類的GetChildControlType 方法 獲取與子標記對應的控件類型的 Type在此方法中,其以tab標簽代替了ServerTab控件,改寫了頁分析邏輯
ControlBuilder類常用的還有AllowWhitespaceLiterals 方法 其指定控件之間是否允許存在空白,大家可以重寫此方法,然后測試下就明白了。
(2)定義一個簡單的ServerTab控件。還須在父控件中重寫AddParsedSubObject方法將ServerTab控件添加到子控件中
protected override void AddParsedSubObject(object obj)

{
if (obj is ServerTab)
base.AddParsedSubObject(obj);
}
(3)最后還需要把控件生成器跟控件關聯起來,當然還要設置ParseChildren(false)
[ControlBuilder(typeof(ServerTabsBuilder))]
[ParseChildren(false)]
public class ServerTabs : WebControl, IPostBackEventHandler

{
}
好了,這里主要代碼就實現了,呈現代碼大家可在后面下載,下面看下頁面代碼

<%
--以上省略css代碼--%>
<custom:ServerTabs
ID="ServerTabs1"
Runat="Server">
<tab Text="First Tab">
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
Contents of the first tab
</tab>
<tab Text="Second Tab">
Contents of the second tab
</tab>
<tab Text="Third Tab">
Contents of the third tab
</tab>
</custom:ServerTabs>
以上鑲套代碼為tab標簽,而非<custom:ServerTabs></custom:ServerTabs>,但實現效果是一樣的,只是我們改了默認的頁分析邏輯,自定義了控件頁生成器(分析器)。看下效果(當重新編譯后需要重新啟動vs2005才能看到效果)

好了,這次的主題也講完了,這里需要注意的是asp.net2.0中復合控件只需要繼承CompositeControl類即可。
上一篇:asp.net控件開發基礎(13)
下一篇:asp.net控件開發基礎(15)