開發樣板化的資料繫結控制項
使用 ASP.NET 資料繫結 (Data Binding) 語法可以很容易地將控制項的屬性繫結於單一的資料項目 (或運算式)。本節將說明開發控制項的更複雜案例,其中控制項的樣板屬性所繫結的資料來源是集合型別 (System.Collections.ICollection 或 System.Collections.IEnumerable)。樣板使網頁開發人員可以自訂已繫結於控制項的資料表示方式。Repeater 和 DataList 控制項是樣板化的資料繫結控制項的範例。
如需 ASP.NET 網頁的資料繫結概觀,請參閱 ASP.NET 快速入門 -> ASP.NET Web Form -> 資料繫結伺服器控制項。如需撰寫樣板控制項的背景資料,請參閱開發樣板化的控制項。
樣板化資料繫結控制項具有型別 ICollection 或 IEnumerable 的資料來源屬性,以及一或多個型別 ITemplate 的屬性。其中一個樣板屬性的容器 (Container) 會定義要繫結資料的屬性 (通常名為 DataItem
)。控制項會在繼承自 Control 的 Databind 方法中實作它的資料繫結邏輯。它會覆寫 CreateChildControls 方法以重新建立回傳時子控制項的階層架構。這些步驟稍後將詳細說明。
若要開發樣板化的資料繫結控制項
定義一個會實作 System.Web.UI.INamingContainer 介面的控制項。
public class TemplatedList : WebControl, INamingContainer {...} [Visual Basic] Public Class TemplatedList Inherits WebControl Implements INamingContainer ... End Class
定義型別 System.Web.UI.ITemplate 的屬性。
[TemplateContainer(typeof(TemplatedListItem))] public virtual ITemplate ItemTemplate { get { return itemTemplate; } set { itemTemplate = value; } } [Visual Basic] <TemplateContainer(GetType(TemplatedListItem))> _ Public Overridable Property ItemTemplate() As ITemplate Get Return _itemTemplate End Get Set _itemTemplate = value End Set End Property
樣板 (在 TemplateContainerAttribute 屬性中指定) 的邏輯容器必須擁有要繫結資料的屬性。通常會將這個屬性命名為 DataItem。如需樣板屬性其邏輯容器的詳細資訊,請參閱開發樣板化的控制項。下列範例將定義樣板屬性的容器。
public class TemplatedListItem : TableRow, INamingContainer { private object dataItem; public virtual object DataItem { get { return dataItem; } set { dataItem = value; } } [Visual Basic] Public Class TemplatedListItem Inherits TableRow Implements INamingContainer Private _dataItem As Object Public Overridable Property DataItem() As Object Get Return _dataItem End Get Set _dataItem = value End Set End Property End Class
覆寫 DataBind 方法 (繼承自 Control) 以提供資料繫結邏輯。這項作業必須完成下列的步驟。
- 叫用 (Invoke) 基底類別 (Base Class) 的 OnDataBinding 方法,以叫用處理常式 (由網頁所附加) 來評估控制項上的資料繫結運算式。
- 清除 Controls 集合。
- 清除子控制項的 ViewState。
- 建立使用該資料來源的子控制項。
- 傳送信號給 ASP.NET 網頁架構以追蹤控制項的 ViewState。
下列程式碼將執行這些步驟。
CreateChildControlsHierarchy
是 Helper 方法,它會執行建立子控制項的實際作業。詳細的資訊,請參閱步驟 5。public override void DataBind() { // Controls with a data-source property perform their // custom data binding by overriding DataBind to // evaluate any data-binding expressions on the control // itself. base.OnDataBinding(EventArgs.Empty); // Reset the control's state. Controls.Clear(); ClearChildViewState(); // Create the control hierarchy using the data source. CreateControlHierarchy(true); ChildControlsCreated = true; TrackViewState(); } [Visual Basic] Public Overrides Sub DataBind() ' Controls with a data-source property perform their custom data ' binding by overriding DataBind. ' Evaluate any data-binding expressions on the control itself. MyBase.OnDataBinding(EventArgs.Empty) ' Reset the control state. Controls.Clear() ClearChildViewState() ' Create the control hierarchy using the data source. CreateControlHierarchy(True) ChildControlsCreated = True TrackViewState() End Sub
覆寫 CreateChildControls 以重新建立回傳案例中的子控制項。其中包含清除 Controls 集合,並使用檢視狀態 (不使用資料來源) 建立控制項階層架構。建立子控制項的實際工作是隱藏於步驟 5 中的
CreateControlHierarchy
方法。protected override void CreateChildControls() { Controls.Clear(); if (ViewState["ItemCount"] != null) { // Create the control hierarchy using the view state, // not the data source. CreateControlHierarchy(false); } } [Visual Basic] Protected Overrides Sub CreateChildControls() Controls.Clear() If Not (ViewState("ItemCount") Is Nothing) Then ' Create the control hierarchy using the view state, ' not the data source. CreateControlHierarchy(False) End If End Sub
定義一個含有 Null 項目的資料來源,並在回傳建立控制項階層架構時使用這個資料來源,而不使用實際的資料來源。步驟 3 和 4 分別使用資料來源和儲存的檢視狀態來建立控制項的階層架構。空的資料來源會使控制項在這兩個步驟的通用項目上實作單一的程式碼路徑。
注意 這個步驟 (步驟 5) 將說明 .NET Framework 的資料繫結 ASP.NET 控制項所使用的實作 (Implementation) 細節。下列程式碼片段中所顯示的
DummyDataSource
類別和CreateControlHierarchy
方法並不屬於 .NET Framework 的部份,但仍可由控制項的開發人員來定義。您並不需要實作這些項目;然而,建議您使用這個範例或類似的方法來提供一個通用的程式碼路徑,以用於建立控制項階層架構。下列程式碼片段將定義一個空的資料來源。
internal sealed class DummyDataSource : ICollection { private int dataItemCount; public DummyDataSource(int dataItemCount) { this.dataItemCount = dataItemCount; } // Implement other methods of the ICollection interface. ... public IEnumerator GetEnumerator() { return new DummyDataSourceEnumerator(dataItemCount); } private class DummyDataSourceEnumerator : IEnumerator { private int count; private int index; public DummyDataSourceEnumerator(int count) { this.count = count; this.index = -1; } public object Current { get { return null; } } // Define other methods of the IEnumerator interface. } } [Visual Basic] NotInheritable Friend Class DummyDataSource Implements ICollection Private dataItemCount As Integer Public Sub New(dataItemCount As Integer) Me.dataItemCount = dataItemCount End Sub ' Implement other methods of the ICollection interface. ... Public Function GetEnumerator() As IEnumerator Implements ICollection.GetEnumerator Return New DummyDataSourceEnumerator(dataItemCount) End Function Private Class DummyDataSourceEnumerator Implements IEnumerator Private count As Integer Private index As Integer Public Sub New(count As Integer) Me.count = count Me.index = - 1 End Sub Public ReadOnly Property Current() As Object Implements IEnumerator.Current Get Return Nothing End Get End Property ' Define other methods of the IEnumerator interface. ... End Class End Class
DummyDataSource
可以用來定義CreateControlHierarchy
方法,如下所示。private void CreateControlHierarchy(bool useDataSource) { IEnumerable dataSource = null; int count = -1; if (useDataSource == false) { // ViewState must have a non-null value for ItemCount because this is checked // by CreateChildControls. count = (int)ViewState["ItemCount"]; if (count != -1) { dataSource = new DummyDataSource(count); } } else { dataSource = this.dataSource; } if (dataSource != null) { int index = 0; count = 0; foreach (object dataItem in dataSource) { ... // Invoke a private helper method to create each item. CreateItem(...); count++; index++; } } if (useDataSource) { // Save the number of items contained for use in round trips. ViewState["ItemCount"] = ((dataSource != null) ? count : -1); } } [Visual Basic] Private Sub CreateControlHierarchy(useDataSource As Boolean) Dim dataSource As IEnumerable = Nothing Dim count As Integer = - 1 If useDataSource = False Then ' ViewState must have a non-null value for ItemCount because this is checked ' by CreateChildControls. count = CInt(ViewState("ItemCount")) If count <> - 1 Then dataSource = New DummyDataSource(count) End If Else dataSource = Me._dataSource End If If Not (dataSource Is Nothing) Then Dim table As New Table() Controls.Add(table) Dim selectedItemIndex As Integer = SelectedIndex Dim index As Integer = 0 count = 0 Dim dataItem As Object For Each dataItem In dataSource Dim itemType As ListItemType = ListItemType.Item If index = selectedItemIndex Then itemType = ListItemType.SelectedItem Else If index Mod 2 <> 0 Then itemType = ListItemType.AlternatingItem End If End If CreateItem(table, index, itemType, useDataSource, dataItem) count += 1 index += 1 Next dataItem End If If useDataSource Then ' Save the number of items contained for use in round trips. If Not (dataSource Is Nothing) Then ViewState("ItemCount") = count Else ViewState("ItemCount") = -1 End If End If End Sub
CreateItem
方法會執行建立樣板和將DataItem
屬性繫結於資料來源的實際作業。下列的程式碼片段將說明如何在樣板化的資料繫結控制項範例中實作CreateItem
方法。請注意CreateItem
方法是一項實作細節且並未在 .NET Framework 中定義。private TemplatedListItem CreateItem(Table table, int itemIndex, ListItemType itemType, bool dataBind, object dataItem) { TemplatedListItem item = new TemplatedListItem(itemIndex, itemType); TemplatedListItemEventArgs e = new TemplatedListItemEventArgs(item); if (itemTemplate != null) { itemTemplate.InstantiateIn(item.Cells[0]); } if (dataBind) { item.DataItem = dataItem; } OnItemCreated(e); table.Rows.Add(item); if (dataBind) { item.DataBind(); OnItemDataBound(e); item.DataItem = null; } return item; } [Visual Basic] Private Function CreateItem(table As Table, itemIndex As Integer, itemType As ListItemType, dataBind As Boolean, dataItem As Object) As TemplatedListItem Dim item As New TemplatedListItem(itemIndex, itemType) Dim e As New TemplatedListItemEventArgs(item) If Not (_itemTemplate Is Nothing) Then _itemTemplate.InstantiateIn(item.Cells(0)) End If If dataBind Then item.DataItem = dataItem End If OnItemCreated(e) table.Rows.Add(item) If dataBind Then item.DataBind() OnItemDataBound(e) item.DataItem = Nothing End If Return item End Function
如需實作本主題所描述步驟的資料繫結控制項範例,請參閱樣板化的資料繫結控制項範例。