ASP.NET 2.0數(shù)據(jù)綁定控件的自定義集合
ASP.NET 2.0數(shù)據(jù)綁定控件:管理自定義集合
ListControl 是一個(gè)過(guò)于專用的類,它以不受您控制的固定方式執(zhí)行數(shù)據(jù)綁定 — 除非您重寫諸如 PerformSelect、OnDataBinding 和 PerformDataBinding 之類的方法。它還提供了預(yù)定義的 Items 集合屬性。讓我們?cè)?ASP.NET 2.0 中的更低級(jí)別處理數(shù)據(jù)綁定,并且設(shè)計(jì)具有下列功能的 ButtonList 控件:
◆使用自定義集合類來(lái)保留組成項(xiàng)
◆用自定義方式管理視圖狀態(tài)
ButtonList 控件是另一個(gè)為每個(gè)綁定數(shù)據(jù)項(xiàng)輸出按鈕的列表控件。您可以讓它從 ListControl 繼承;而且,您可以獲得 HeadlineList 的源代碼,將 Label 替換為 Button,而它仍然應(yīng)當(dāng)正常工作。這一次,我將采用一種不同的方法來(lái)說(shuō)明 DataBoundControl 的行為。為簡(jiǎn)單起見(jiàn),我仍將跳過(guò) IRepeatInfoUser 接口。
- public class ButtonList : System.Web.UI.WebControls.DataBoundControl
- {
- :
- }
標(biāo)題和命令名稱表現(xiàn)了每個(gè)按鈕的性質(zhì)。該信息是通過(guò)幾個(gè)自定義屬性(如 DataTextField 和 DataCommandField)從綁定數(shù)據(jù)源中獲得的。您可以容易地添加類似的屬性,以提供數(shù)據(jù)綁定工具提示,甚至提供 URL。
- public virtual string DataCommandField
- {
- get
- {
- object o = ViewState["DataCommandField"];
- if (o == null)
- return "";
- return (string)o;
- }
- set { ViewState["DataCommandField"] = value; }
- }
所發(fā)現(xiàn)的有關(guān)每個(gè)綁定按鈕的所有信息都被填充到一個(gè)通過(guò) Items 屬性公開(kāi)的自定義對(duì)象集合中。(請(qǐng)注意,Items 只是該屬性的標(biāo)準(zhǔn)、慣用而任意的名稱。)
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
- [PersistenceMode(PersistenceMode.InnerProperty)]
- public virtual ButtonItemCollection Items
- {
- get
- {
- if (_items == null)
- {
- _items = new ButtonItemCollection();
- if (base.IsTrackingViewState)
- _items.TrackViewState();
- }
- return _items;
- }
- }
Items 集合是自定義 ButtonItemCollection 類的實(shí)例 — ButtonItem 對(duì)象的集合。ButtonItem 類只是存儲(chǔ)了有關(guān)綁定按鈕的關(guān)鍵信息 — Text 和 CommandName 屬性,外加幾個(gè)構(gòu)造函數(shù)以及 ToString 方法。ButtonItem 類是作為普通列表控件的 ListItem 類的對(duì)等物。下面是一個(gè)示例。
- public class ButtonItem
- {
- private string _text;
- private string _command;
- public ButtonItem(string text, string command) {
- _text = text;
- _command = command;
- }
- public string Text {
- get {return _text;}
- set {_text = value;}
- }
- public string CommandName {
- get { return _command; }
- set { _command = value; }
- }
- public override string ToString() {
- return "Button [" + Text + "]";
- }
- }
現(xiàn)在,如何創(chuàng)建 ButtonItem 對(duì)象的集合呢?在 ASP.NET 1.x 中,您必須生成一個(gè)從 CollectionBase 繼承的自定義集合類,并且起碼重寫幾個(gè)方法。然而,自定義集合只是圍繞 ArrayList 對(duì)象的包裝而已,在訪問(wèn)速度方面并沒(méi)有任何真正的優(yōu)勢(shì)。實(shí)際上,仍然需要進(jìn)行轉(zhuǎn)換。.NET 2.0 中的泛型提供了真正的轉(zhuǎn)折點(diǎn)。要生成 ButtonItem 對(duì)象集合,您需要以下代碼:
- public class ButtonItemCollection : Collection < ButtonItem>
- {
- }
并且,它的性能也會(huì)更好,因?yàn)榫幾g器在幕后完成了某些工作。ButtonList 控件只需要兩個(gè)被重寫的方法:Render 和 PerformDataBinding。Render 假定 Items 集合被填充;因此,它只是進(jìn)行迭代并輸出標(biāo)記代碼。
- protected override void Render(HtmlTextWriter writer)
- {
- for(int i=0; i< } btn.RenderControl(writer); btn.CommandName="item.CommandName;" btn.Text="item.Text;" Button(); btn="new" Button item="Items[i];" ButtonItem { i++)>
ASP.NET 2.0數(shù)據(jù)綁定控件:Items集合的重要性
Items 集合為什么如此重要?它可以幫助您獲得兩個(gè)結(jié)果。首先,您可以用手動(dòng)添加的項(xiàng)填充該列表控件。其次,一旦在視圖狀態(tài)中持久保存該集合,您就可以在回發(fā)時(shí)重新生成該控件的用戶界面,而無(wú)須綁定到數(shù)據(jù)。在進(jìn)行數(shù)據(jù)綁定時(shí),Items 集合是在何處以及由誰(shuí)填充的呢?這需要用到 PerformDataBinding。該方法獲得一個(gè)可枚舉的數(shù)據(jù)列表(無(wú)論原始數(shù)據(jù)源是什么)并使用它來(lái)填充 Items 集合。
- protected override void PerformDataBinding(IEnumerable dataSource)
- {
- base.PerformDataBinding(dataSource);
- string textField = DataTextField;
- string commandField = DataCommandField;
- if (dataSource != null) {
- foreach (object o in dataSource)
- {
- ButtonItem item = new ButtonItem();
- item.Text = DataBinder.GetPropertyValue(o, textField, null);
- item.CommandName = DataBinder.GetPropertyValue(o,
- DataCommandField, null);
- Items.Add(item);
- }
- }
- }
每當(dāng)需要進(jìn)行數(shù)據(jù)綁定時(shí),該方法都能夠確保 Items 集合被填充。在回發(fā)時(shí)會(huì)發(fā)生什么?在這種情況下,必須根據(jù)視圖狀態(tài)重新構(gòu)建 Items 集合。您可以通過(guò) IStateManager 接口上的方法賦予自定義集合類這一能力。以下為該接口的關(guān)鍵方法:
- public void LoadViewState(object state)
- {
- if (state != null) {
- Pair p = (Pair) state;
- Clear();
- string[] rgText = (string[])p.First;
- string[] rgCommand = (string[])p.Second;
- for (int i = 0; i < rgText.Length; i++)
- Add(new ButtonItem(rgText[i], rgCommand[i]));
- }
- }
- public object SaveViewState()
- {
- int numOfItems = Count;
- object[] rgText = new string[numOfItems];
- object[] rgCommand = new string[numOfItems];
- for (int i = 0; i < numOfItems; i++) {
- rgText[i] = this[i].Text;
- rgCommand[i] = this[i].CommandName;
- }
- return new Pair(rgText, rgCommand);
- }
該類使用一個(gè) Pair 對(duì)象(一種經(jīng)過(guò)優(yōu)化的 2 位置數(shù)組)將自身序列化為視圖狀態(tài)。您需要?jiǎng)?chuàng)建兩個(gè)對(duì)象數(shù)組,以便保留每個(gè)按鈕的文本和命令名稱。這兩個(gè)數(shù)組隨后被成對(duì)打包并插入到該視圖狀態(tài)中。當(dāng)還原該視圖狀態(tài)時(shí),會(huì)將該數(shù)組對(duì)拆包,并且使用先前存儲(chǔ)的信息重新填充 Items 集合。使用該方法要比使 ButtonItem 類可序列化更可取,因?yàn)閭鹘y(tǒng)的二進(jìn)制格式化程序的性能(在空間和時(shí)間這兩個(gè)方面)更差。
然而,向集合中添加視圖狀態(tài)支持還不夠。還必須增強(qiáng) ButtonList 控件以利用集合的序列化功能。您可以重寫控件類上的 LoadViewState 和 SaveViewState。
- protected override void LoadViewState(object savedState)
- {
- if (savedState != null) {
- Pair p = (Pair) savedState;
- base.LoadViewState(p.First);
- Items.LoadViewState(p.Second);
- }
- else
- base.LoadViewState(null);
- }
- protected override object SaveViewState()
- {
- object baseState = base.SaveViewState();
- object itemState = Items.SaveViewState();
- if ((baseState == null) && (itemState == null))
- return null;
- return new Pair(baseState, itemState);
- }
控件的視圖狀態(tài)由兩個(gè)元素組成:默認(rèn)控件的視圖狀態(tài)以及 Items 集合。這兩個(gè)對(duì)象被打包到 Pair 對(duì)象中。除了 Pair 對(duì)象以外,您還可以使用 Triplet 對(duì)象(包含三個(gè)對(duì)象的數(shù)組),或者使用 Pair 或 Triplet 對(duì)組成任意數(shù)量的對(duì)象。
以這種方式設(shè)計(jì)的自定義集合還可以在設(shè)計(jì)時(shí)滿足需要。Visual Studio 2005 中嵌入的默認(rèn)集合編輯器可以識(shí)別該集合并彈出如圖 3 所示的對(duì)話框。
ASP.NET 2.0數(shù)據(jù)綁定控件:設(shè)計(jì)時(shí)的 ButtonList Items 集合
值得說(shuō)明的是,在 ASP.NET 2.0 中,某些數(shù)據(jù)綁定控件使您可以將數(shù)據(jù)綁定項(xiàng)與以編程方式通過(guò) Items 集合添加的項(xiàng)分開(kāi)。布爾型的 AppendDataBoundItems 屬性用于控制該控件的編程接口的這一方面。該屬性在 ListControl(而非 DataBoundControl)上定義,并且默認(rèn)為 false。
【編輯推薦】