template 宣言のあるデータ連結コントロールの開発
ASP.NET データ連結構文を使用して、コントロールのプロパティを単一のデータ項目または式に連結するのは簡単です。ここでは、System.Collections.ICollection コレクション型または System.Collections.IEnumerable コレクション型であるデータ ソースに連結された template 宣言のあるプロパティを備えたコントロール開発の、より複雑なシナリオについて説明します。テンプレートを使用すると、ページ開発者は、コントロールに連結されたデータのプレゼンテーションをカスタマイズできます。Repeater および DataList コントロールは、template 宣言のあるデータ連結コントロールの例です。
ASP.NET ページでのデータ連結の概要については、「ASP.NET クイック スタート」の「.NET サンプル - ASP.NET のデータ連結」でデータ連結のサンプルを参照してください。template 宣言のあるコントロール作成のバックグラウンドについては、「テンプレート コントロールの開発」を参照してください。
template 宣言のあるデータ連結コントロールは、ICollection 型または IEnumerable 型のデータ ソース プロパティを 1 つと ITemplate 型のプロパティを 1 つ以上備えています。テンプレート プロパティのコンテナは、データ連結先のプロパティ (通常は DataItem
という名前) を定義します。template 宣言のあるデータ連結コントロールは、Control から継承した Databind メソッドにデータ連結ロジックを実装します。template 宣言のあるデータ連結コントロールは、ポストバック時に CreateChildControls メソッドをオーバーライドして子コントロールの階層を再作成します。これらの手順の詳細を次に説明します。
template 宣言のあるデータ連結コントロールを開発するには、次のようにします。
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
Control から継承した DataBind メソッドをオーバーライドしてデータ連結ロジックを提供します。これは、次の手順から構成されます。
- 基本クラスの OnDataBinding メソッドを呼び出して、コントロールのデータ連結式を評価する、ページによって追加されたハンドラを呼び出します。
- Controls コレクションをクリアします。
- 子コントロールの ViewState をクリアします。
- データ ソースを使用して子コントロールを作成します。
- ASP.NET ページ フレームワークに通知してコントロールの ViewState を追跡します。
これらの手順を実行するコードを次に示します。
CreateChildControlsHierarchy
は、子コントロール作成の実際の作業を行うヘルパ メソッドです。詳細については、手順 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 によって、データ ソースと保存されたビューステートをそれぞれ使用して、コントロール階層が作成されます。コントロールは、ダミー データ ソースにより、これら 2 つの手順の共通要素の単一コード パスを実装できます。
メモ この手順 (手順 5) では、.NET Framework のデータ連結 ASP.NET コントロールが使用する実装の詳細について説明しています。次のコード片に示す
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
テンプレートの作成とデータ ソースへの
DataItem
プロパティの連結の実際の作業は、CreateItem
メソッドが行います。「template 宣言のあるデータ連結コントロールのサンプル」における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
このトピックで説明した手順を実装するデータ連結コントロールのサンプルについては、「template 宣言のあるデータ連結コントロールのサンプル」を参照してください。