ASP.NET復(fù)合控件引發(fā)數(shù)據(jù)綁定事件
生成數(shù)據(jù)綁定ASP.NET復(fù)合控件
大多數(shù)復(fù)雜的服務(wù)器控件都已綁定數(shù)據(jù)(也可能已經(jīng)模板化),并且由各種子控件構(gòu)成。這些控件保留了一個構(gòu)成項(通常為表的行或單元格)的列表。該列表在經(jīng)過回發(fā)后會保存在視圖狀態(tài)中,并且從綁定數(shù)據(jù)生成或從視圖狀態(tài)重建。該控件還在視圖狀態(tài)中保存其構(gòu)成項的數(shù)量,以便在頁面中其他控件引起回發(fā)時可以正確重建表結(jié)構(gòu)。我將用 DataGrid 控件舉例說明。
DataGrid 由一列行構(gòu)成,每一行都代表綁定數(shù)據(jù)源中的一個記錄。每個網(wǎng)格行都通過一個 DataGridRow 對象(從 TableRow 派生的一個類)表示。在各網(wǎng)格行創(chuàng)建完成并被添加到最終網(wǎng)格表時,諸如 ItemCreated 和 ItemDataBound 之類的相應(yīng)事件將被引發(fā)至頁面。當(dāng)通過數(shù)據(jù)綁定創(chuàng)建 DataGrid 時,其行數(shù)由綁定項數(shù)和頁面大小決定。如果帶有 DataGrid 的頁面回發(fā)會怎樣?
這種情況下,如果是由 DataGrid 自身引起的回發(fā)(例如,用戶單擊以進行排序或標頁),則新頁面會再次通過數(shù)據(jù)綁定來呈現(xiàn) DataGrid。這是顯而易見的,因為 DataGrid 需要刷新數(shù)據(jù)進行顯示。如果是主頁回發(fā),則情況就不同了,因為單擊了頁面上的另一個控件(例如某按鈕)。這種情況下,DataGrid 不綁定到數(shù)據(jù)并且必須從視圖狀態(tài)進行重建。(如果禁用了視圖狀態(tài),就是另外一種情況了,這時只能通過數(shù)據(jù)綁定顯示網(wǎng)格。)
數(shù)據(jù)源不保存在視圖狀態(tài)中。作為復(fù)合控件,DataGrid 包含子控件,其中每個子控件都將自己的狀態(tài)保存到視圖狀態(tài)并從視圖狀態(tài)恢復(fù)。DataGrid 只需跟蹤在所有行和所包含控件從視圖狀態(tài)恢復(fù)之前它所必須重復(fù)執(zhí)行的次數(shù)。此次數(shù)與所顯示綁定項的數(shù)量一致,并且必須作為控件狀態(tài)的一部分存儲到視圖狀態(tài)中。在 ASP.NET 1.x 中,您必須自己學(xué)習(xí)并實現(xiàn)此模式。在 ASP.NET 2.0 中,從新類 CompositeDataBoundControl 派生您的復(fù)合控件就可以了。
讓我們嘗試使用一種顯示可擴展數(shù)據(jù)綁定新聞標題行的網(wǎng)格類控件。在此過程中,我們將再度使用在前文中論及的 Headline 控件。
- public class HeadlineListEx :CompositeDataBoundControl
- {
- :
- }
HeadlineListEx 控件包含了一個收集了所有綁定數(shù)據(jù)項的 Items 集合屬性。該集合為公共集合,并且可在與多數(shù)列表控件一起運行時通過編程方式填充。對典型數(shù)據(jù)綁定的支持是通過一對屬性(DataTextField 和 DataTitleField)實現(xiàn)的。這兩個屬性表明了數(shù)據(jù)源中將用于填充新聞標題和文本的字段。Items 集合被保存到視圖狀態(tài)中。
要將 HeadlineListEx 控件轉(zhuǎn)換為真正的ASP.NET復(fù)合控件,您首先需要從 CompositeDataBoundControl 將其派生出來,然后再替換 CreateChildControls。有意思的是,你會注意到 CreateChildControls 是重載方法。
- override int CreateChildControls()
- override int CreateChildControls(IEnumerable data, bool dataBinding)
***個重載方法替換了在 Control 類中定義的方法。第二個重載方法是每個復(fù)合控件都必須替換的一種抽象方法。實際上,復(fù)合控件的開發(fā)工作簡化為兩大主要任務(wù):
替換 CreateChildControls。
實現(xiàn) Rows 集合屬性以跟蹤控件的所有構(gòu)成項。
Rows 屬性不同于 Items,因為它不保存在視圖狀態(tài)中,且具有與請求相同的生存期,并引用幫助程序?qū)ο蠖皇墙壎〝?shù)據(jù)項。
- public virtual HeadlineRowCollection Rows
- {
- get
- {
- if (_rows == null)
- _rows = new HeadlineRowCollection();
- return _rows;
- }
- }
Rows 集合在控件生成時填充。讓我們看一下 CreateChildControls 的替換方法。該方法采用了兩個參數(shù):綁定項和一個布爾標記,其中布爾標記用于指明該控件是通過數(shù)據(jù)綁定創(chuàng)建還是通過視圖狀態(tài)創(chuàng)建。(請注意示例程序文件中的程序員注釋使用的是英文,本文中將其譯為中文是為了便于參考。)
- override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
- {
- if (dataBinding)
- {
- string textField = DataTextField;
- string titleField = DataTitleField;
- if (dataSource != null)
- {
- foreach (object o in dataSource)
- {
- HeadlineItem elem = new HeadlineItem();
- elem.Text = DataBinder.GetPropertyValue(o, textField, null);
- elem.Title = DataBinder.GetPropertyValue(o, titleField, null);
- Items.Add(elem);
- }
- }
- }
- // 開始生成控件層次結(jié)構(gòu)
- Table t = new Table();
- Controls.Add(t);
- Rows.Clear();
- int itemCount = 0;
- foreach(HeadlineItem item in Items)
- {
- HeadlineRowType type = HeadlineRowType.Simple;
- HeadlineRow row = CreateHeadlineRow(t, type,
- item, itemCount, dataBinding);
- _rows.Add(row);
- itemCount++;
- }
- return itemCount;
- }
在數(shù)據(jù)綁定的情況下,首先要填充 Items 集合。遍歷綁定集合,提取數(shù)據(jù),然后填充 HeadlineItem 類的新建實例。接下來,遍歷 Items 集合(該集合中可能包含以編程方式添加的附加項),并在控件中創(chuàng)建行。
- HeadlineRow CreateHeadlineRow(Table t, HeadlineRowType rowType,
- HeadlineItem dataItem, int index, bool dataBinding)
- {
- // 為最外部表創(chuàng)建新行
- HeadlineRow row = new HeadlineRow(rowType);
- // 為子控件創(chuàng)建單元格
- TableCell cell = new TableCell();
- row.Cells.Add(cell);
- Headline item = new Headline();
- cell.Controls.Add(item);
- // 此時引發(fā) HeadlineRowCreated 事件
- // 將此行添加到所創(chuàng)建的 HTML 表
- t.Rows.Add(row);
- // 處理數(shù)據(jù)對象綁定
- if (dataBinding)
- {
- row.DataItem = dataItem;
- Headline ctl = (Headline) cell.Controls[0];
- ctl.Text = dataItem.Text;
- ctl.Title = dataItem.Title;
- // 此時引發(fā) HeadlineRowDataBound 事件
- }
- return row;
- }
CreateHeadlineRow 方法會創(chuàng)建并返回 HeadlineRow 類(從 TableRow 派生而來)的一個實例。在這種情況下,此行會包含一個由 Headline 控件填充的單元格。在其他情況下,您可以更改此部分代碼以根據(jù)需要添加多個單元格并相應(yīng)填充內(nèi)容。
重要的是,要將所需完成的任務(wù)分為兩個不同的步驟:創(chuàng)建和數(shù)據(jù)綁定。首先,創(chuàng)建行的布局,引發(fā)行創(chuàng)建事件(如果有),并***將其添加到父表中。接下來,如果要將控件綁定到數(shù)據(jù),則設(shè)置對綁定數(shù)據(jù)敏感的子控件屬性。完成操作后,則引發(fā)一個行數(shù)據(jù)綁定事件(如果有)。
請注意,該模式更準確描述了ASP.NET復(fù)合控件的內(nèi)部體系結(jié)構(gòu)。
可以使用以下代碼來引發(fā)事件。
- HeadlineRowEventArgs e = new HeadlineRowEventArgs();
- e.DataItem = dataItem;
- e.RowIndex = index;
- e.RowType = rowType;
- e.Item = row;
- OnHeadlineRowDataBound(e);
請注意,只在要引發(fā)數(shù)據(jù)綁定事件時才設(shè)置 DataItem 屬性。事件數(shù)據(jù)結(jié)構(gòu)被任意設(shè)置為以下形式。如果您認為有必要,盡可以對其進行更改。
- public class HeadlineRowEventArgs :EventArgs
- {
- public HeadlineItem DataItem;
- public HeadlineRowType RowType;
- public int RowIndex;
- public HeadlineRow Item;
- }
若要實際引發(fā)一個事件,通常的做法是使用一個如下定義的受保護方法。
- protected virtual void OnHeadlineRowDataBound(HeadlineRowEventArgs e)
- {
- if (HeadlineRowDataBound != null)
- HeadlineRowDataBound(this, e);
- }
若要聲明此事件,可在 ASP.NET 2.0 中使用新的一般事件處理程序委托。
- public event EventHandler< HeadlineRowEventArgs> HeadlineRowDataBound;
在示例頁中,一切均照常執(zhí)行。您可在控件標記上定義處理程序并將某方法寫入代碼文件。示例如下。
- < cc1:HeadlineListEx runat="server" ID="HeadlineListEx1"
- DataTextField="notes" DataTitleField="lastname"
- DataSourceID="MySource" OnHeadlineRowDataBound="HeadlineRowCreated" />
HeadlineRowCreated 事件處理程序的代碼顯示如下。
- protected void HeadlineRowCreated(object sender, HeadlineRowEventArgs e)
- {
- if (e.DataItem.Title.Contains("Doe"))
- e.Item.BackColor = Color.Red;
- }
圖 7:運行中的 HeadlineListEx 控件
通過掛接數(shù)據(jù)綁定事件,所有含有 Doe 的項都將以紅色背景呈現(xiàn)。
【編輯推薦】