DataList または Repeater コントロールのデータを並べ替える (C#)
このチュートリアルでは、DataList と Repeater に並べ替えのサポートを組み込む方法と、データのページングと並べ替えが可能な DataList または Repeater を構築する方法について説明します。
はじめに
前のチュートリアルでは、DataList にページングのサポートを実装する手順を確認しました。 ProductsBLL
クラス (GetProductsAsPagedDataSource
) に新しいメソッドを作成し、PagedDataSource
オブジェクトを返すようにしました。 DataList または Repeater にバインドすると、DataList または Repeater には、要求されたページのデータのみが表示されます。 この手法は、GridView、DetailsView、FormView の各コントロールが既定の組み込みページング機能を提供するために内部で使用しているものと同様です。
GridView は、ページング サポートだけでなく、すぐに使える組み込みの並べ替えの機能も提供しています。 DataList と Repeater のどちらも組み込みの並べ替え機能は組み込まれていませんが、少しのコードを追加することで並べ替え機能を実装できます。 このチュートリアルでは、DataList と Repeater に並べ替えのサポートを組み込む方法と、データのページングと並べ替えが可能な DataList または Repeater を構築する方法について説明します。
並べ替えのレビュー
「レポート データのページングと並べ替え」チュートリアルで説明したように、GridView コントロールにはすぐに使える組み込みの並べ替え機能が用意されています。 各 GridView フィールドには、データを並べ替える基準となるデータ フィールドを指定する SortExpression
が関連付けられています。 GridView の true
プロパティが AllowSorting
に設定されている場合、SortExpression
プロパティ値の設定されたフィールドのヘッダー部分が LinkButton として表示されます。 ユーザーが特定の GridView フィールドのヘッダーをクリックすると、クリックしたフィールドの SortExpression
に基づいてデータが並べ替えられ、ポストバックが発生します。
また、GridView コントロールには SortExpression
プロパティがあり、データの並べ替えの基準となる GridView フィールドの SortExpression
が格納されます。 加えて、SortDirection
プロパティによって、データを昇順で並べ替えるか降順で並べ替えるかが決定されます (ユーザーが GridView フィールドのヘッダー リンクを連続して 2 回クリックすると、並べ替え順序が切り替わります)。
GridView コントロールがデータ ソース コントロールにバインドされると、SortExpression
プロパティと SortDirection
プロパティがデータ ソース コントロールに渡されます。 データ ソース コントロールは、データを取得し、渡された SortExpression
プロパティと SortDirection
プロパティに基づいて並べ替えを行います。 データの並べ替えが完了すると、データ ソース コントロールは並べ替えられたデータを GridView に返します。
DataList や Repeater コントロールでこの機能を実現するには、次のことを行う必要があります。
- 並べ替え用のインターフェイスを作成する
- 並べ替えの基準となるデータ フィールドと、昇順で並べ替えるか降順で並べ替えるかを記憶しておく
- ObjectDataSource に特定のデータ フィールドでデータを並べ替えるように指示する
これら 3 つの作業については、手順 3 と手順 4 で取り上げます。 その後、DataList や Repeater でページングと並べ替えの両方をサポートする方法について説明します。
手順 2: Repeater で製品を表示する
並べ替え関連の機能を実装する前に、まず製品を Repeater コントロールに一覧表示することから始めましょう。 まず、PagingSortingDataListRepeater
フォルダーの Sorting.aspx
ページを開きます。 Web ページに Repeater コントロールを追加し、その ID
プロパティを SortableProducts
に設定します。 Repeater のスマート タグから、ProductsDataSource
という名前の新しい ObjectDataSource を作成し、ProductsBLL
クラスの GetProducts()
メソッドからデータを取得するように構成します。 [挿入]、[更新]、[削除] の各タブのドロップダウン リストから [(なし)] オプションを選択します。
図 1: ObjectDataSource を作成し、GetProductsAsPagedDataSource()
メソッドを使用するように構成する (クリックするとフルサイズの画像が表示されます)
図 2: [挿入]、[更新]、[削除] タブのドロップダウン リストを [(なし)] に設定する (クリックするとフルサイズの画像が表示されます)
DataList とは異なり、Visual Studio では Repeater コントロールをデータ ソースにバインドしても、自動的に ItemTemplate
は作成されません。 また、この ItemTemplate
は宣言的に追加する必要があります。Repeater コントロールのスマート タグには、DataList にあるような [テンプレートの編集] オプションがないためです。 前のチュートリアルで使用したのと同じ ItemTemplate
を使用しましょう。これは、製品の名前、仕入先、カテゴリを表示するものでした。
ItemTemplate
を追加すると、Repeater と ObjectDataSource の宣言型マークアップは次のようになるはずです。
<asp:Repeater ID="SortableProducts" DataSourceID="ProductsDataSource"
EnableViewState="False" runat="server">
<ItemTemplate>
<h4><asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>'></asp:Label></h4>
Category:
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Eval("CategoryName") %>'></asp:Label><br />
Supplier:
<asp:Label ID="SupplierNameLabel" runat="server"
Text='<%# Eval("SupplierName") %>'></asp:Label><br />
<br />
<br />
</ItemTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProducts">
</asp:ObjectDataSource>
図 3 は、このページをブラウザーで表示した場合の画面を示しています。
図 3: 各製品の名前、仕入先、カテゴリが表示されている (クリックするとフルサイズの画像が表示されます)
手順 3: ObjectDataSource にデータの並べ替えを指示する
Repeater に表示されるデータを並べ替えるには、データの並べ替えに使用する式を ObjectDataSource に伝える必要があります。 ObjectDataSource がデータを取得する前に、まず Selecting
イベント が発生します。これにより、並べ替え式を指定する機会が与えられます。 Selecting
イベント ハンドラーには ObjectDataSourceSelectingEventArgs
型のオブジェクトが渡され、このオブジェクトには DataSourceSelectArguments
型の Arguments
という名前のプロパティがあります。 DataSourceSelectArguments
クラスは、データのコンシューマーからデータ ソース コントロールにデータ関連の要求を渡すように設計されており、SortExpression
プロパティ が含まれています。
ASP.NET ページから ObjectDataSource に並べ替え情報を渡すには、Selecting
イベントのイベント ハンドラーを作成し、次のコードを使用します。
protected void ProductsDataSource_Selecting
(object sender, ObjectDataSourceSelectingEventArgs e)
{
e.Arguments.SortExpression = sortExpression;
}
sortExpression 値には、データの並べ替えに使用するデータ フィールドの名前 (ProductName など) を割り当てる必要があります。 並べ替えの方向に関連するプロパティがないため、データを降順で並べ替えるには、sortExpression 値の末尾に文字列 DESC を追加します (例: ProductName DESC)。
sortExpression にいくつかのハードコーディングされた値を試し、ブラウザーで結果を確認してみてください。 図 4 に示すように、sortExpression として ProductName DESC を使用すると、製品が名前のアルファベットの逆順に並べ替えられます。
図 4: 製品が名前のアルファベットの逆順で並べ替えられている (クリックするとフルサイズの画像が表示されます)
手順 4: 並べ替え用のインターフェイスを作成し、並べ替え式と方向を保持する
GridView で並べ替え機能を有効にすると、並べ替え可能な各フィールドのヘッダー テキストが LinkButton に変わり、クリックするとデータがそれに応じて並べ替えられるようになります。 このような並べ替え用のインターフェイスは、データが列に整然と配置されている GridView には適しています。 ただし、DataList や Repeater コントロールでは、別の並べ替え用のインターフェイスが必要です。 データをグリッド形式ではなくリスト形式で表示する場合、データの並べ替えに使用できるフィールドを選択するためのドロップダウン リストが並べ替え用のインターフェイスとしてよく使われます。 このチュートリアルではそのようなインターフェイスを実装してみましょう。
DropDownList Web コントロールを SortableProducts
Repeater の上に追加し、その ID
プロパティを SortBy
に設定します。 プロパティ ウィンドウで Items
プロパティの省略記号をクリックし、ListItem コレクション エディターを開きます。 ProductName
、CategoryName
、SupplierName
の各フィールドでデータを並べ替えるための ListItem
を追加します。 また、ListItem
を追加して、製品名をアルファベットの逆順で並べ替えます。
ListItem
Text
プロパティは任意の値 (Name など) に設定できますが、Value
プロパティにはデータ フィールドの名前 (ProductName など) を設定する必要があります。 結果を降順で並べ替えるには、データ フィールド名の末尾に文字列 DESC を追加します (例: ProductName DESC)。
図 5: 並べ替え可能な各データ フィールドに ListItem
を追加する
最後に、DropDownList の右側に Button Web コントロールを追加します。 その ID
を RefreshRepeater
に設定し、Text
プロパティを Refresh に設定します。
ListItem
を作成して [更新] ボタンを追加すると、DropDownList と Button の宣言型構文は次のようになります。
<asp:DropDownList ID="SortBy" runat="server">
<asp:ListItem Value="ProductName">Name</asp:ListItem>
<asp:ListItem Value="ProductName DESC">Name (Reverse Order)
</asp:ListItem>
<asp:ListItem Value="CategoryName">Category</asp:ListItem>
<asp:ListItem Value="SupplierName">Supplier</asp:ListItem>
</asp:DropDownList>
<asp:Button runat="server" ID="RefreshRepeater" Text="Refresh" />
並べ替え用の DropDownList が完成したら、次は ObjectDataSource の Selecting
イベント ハンドラーを更新し、ハードコーディングされた並べ替え式ではなく、選択された SortBy``ListItem
の Value
プロパティを使用するようにする必要があります。
protected void ProductsDataSource_Selecting
(object sender, ObjectDataSourceSelectingEventArgs e)
{
// Have the ObjectDataSource sort the results by the selected
// sort expression
e.Arguments.SortExpression = SortBy.SelectedValue;
}
この時点で最初にページにアクセスすると、既定で選択されているSortBy
ListItem
であるため、製品は最初にProductName
データ フィールドで並べ替えられます (図 6 を参照)。 カテゴリなどの別の並べ替えオプションを選択して [更新] をクリックすると、ポストバックが発生し、カテゴリ名でデータが再度並べ替えられます (図 7 参照)。
図 6: 製品は最初、名前で並べ替えられている (クリックするとフルサイズの画像が表示されます)
図 7: 製品がカテゴリ別に並べ替えられるようになった (クリックするとフルサイズの画像が表示されます)
Note
[更新] ボタンをクリックすると、Repeater のビュー ステートが無効になっているため、データが自動的に再度並べ替えられます。これにより、ポストバックが発生するたびに Repeater がデータ ソースに再バインドされるようになります。 Repeater のビュー ステートを有効にしたままにしておくと、並べ替えのドロップダウン リストを変更しても、並べ替え順序には影響しません。 これを修正するには、[更新] ボタンの Click
イベントのイベント ハンドラーを作成し、Repeater をデータ ソースに再バインドします (Repeater の DataBind()
メソッドを呼び出します)。
並べ替え式と方向を保持する
並べ替えに関係しないポストバックが発生する可能性のあるページで、並べ替え可能な DataList や Repeater を作成する場合、並べ替え式と方向をポストバックの前後で保持しておくことが不可欠です。 たとえば、このチュートリアルの Repeater を更新し、各製品に [削除] ボタンを追加することを想像してみてください。 ユーザーが [削除] ボタンをクリックすると、選択した製品を削除し、データを Repeater に再バインドするコードを実行します。 並べ替えの詳細がポストバックの前後で保持されていない場合、画面に表示されるデータは元の並べ替え順序に戻ります。
このチュートリアルでは、DropDownList が並べ替え式と方向をビュー ステートに暗黙的に保存してくれます。 さまざまな並べ替えオプションを提供する LinkButtons などを備えた別の並べ替え用のインターフェイスを使用している場合は、ポストバックの前後で並べ替え順序を保持するように注意する必要があります。 これは、並べ替えパラメーターをページのビュー ステートに保存するか、並べ替えパラメーターをクエリ文字列に含めるか、または他の状態を保持する手法を用いることで実現できます。
このチュートリアルの今後の例では、ページのビュー ステートに並べ替えの詳細を保持する方法について説明します。
手順 5: 既定のページングを使用する DataList に並べ替えのサポートを追加する
前のチュートリアル では、DataList を使用して既定のページングを実装する方法について説明しました。 前述の例を発展させて、ページングされたデータを並べ替えられるようにしてみましょう。 最初に、PagingSortingDataListRepeater
フォルダーの SortingWithDefaultPaging.aspx
と Paging.aspx
のページを開きます。 Paging.aspx
ページで [ソース] ボタンをクリックすると、ページの宣言型マークアップが表示されます。 選択したテキスト (図 8 を参照) をコピーし、SortingWithDefaultPaging.aspx
の <asp:Content>
タグ間の宣言型マークアップに貼り付けます。
図 8: Paging.aspx
から SortingWithDefaultPaging.aspx
までの <asp:Content>
タグ内の宣言型マークアップを複製する (クリックするとフルサイズの画像が表示されます)
宣言型マークアップをコピーしたら、Paging.aspx
ページの分離コード クラスに含まれているメソッドとプロパティを SortingWithDefaultPaging.aspx
の分離コード クラスにコピーします。 次に、ブラウザーで SortingWithDefaultPaging.aspx
ページを表示してみてください。 Paging.aspx
と同じ機能と外観が表示されるはずです。
既定のページングと並べ替えメソッドを含むように ProductsBLL を拡張する
前のチュートリアルでは、ProductsBLL
クラスに PagedDataSource
オブジェクトを返す GetProductsAsPagedDataSource(pageIndex, pageSize)
メソッドを作成しました。 この PagedDataSource
オブジェクトには、すべて の製品が設定されました (BLL の GetProducts()
メソッドを使用)。ただし、DataList にバインドすると、指定された pageIndex と pageSize の入力パラメーターに対応するレコードのみが表示されました。
このチュートリアルの前半では、ObjectDataSource の Selecting
イベント ハンドラーから並べ替え式を指定することで、並べ替えのサポートを追加しました。 これは、GetProducts()
メソッドが返す ProductsDataTable
のように、並べ替え可能なオブジェクトが ObjectDataSource に返される場合にはうまく機能します。 ただし、GetProductsAsPagedDataSource
メソッドが返す PagedDataSource
オブジェクトは、内部データ ソースの並べ替えをサポートしていません。 その代わりに、GetProducts()
メソッドから返される結果を PagedDataSource
に格納する前に、並べ替える必要があります。
これを実現するには、ProductsBLL
クラスに新しいメソッド GetProductsSortedAsPagedDataSource(sortExpression, pageIndex, pageSize)
を作成します。 GetProducts()
メソッドが返す ProductsDataTable
を並べ替えるには、その既定の DataTableView
の Sort
プロパティを指定します。
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public PagedDataSource GetProductsSortedAsPagedDataSource
(string sortExpression, int pageIndex, int pageSize)
{
// Get ALL of the products
Northwind.ProductsDataTable products = GetProducts();
// Sort the products
products.DefaultView.Sort = sortExpression;
// Limit the results through a PagedDataSource
PagedDataSource pagedData = new PagedDataSource();
pagedData.DataSource = products.DefaultView;
pagedData.AllowPaging = true;
pagedData.CurrentPageIndex = pageIndex;
pagedData.PageSize = pageSize;
return pagedData;
}
GetProductsSortedAsPagedDataSource
メソッドは、前のチュートリアルで作成した GetProductsAsPagedDataSource
メソッドとは若干異なります。 具体的には、GetProductsSortedAsPagedDataSource
は追加の入力パラメーター sortExpression
を受け取り、この値を ProductDataTable
の DefaultView
の Sort
プロパティに代入します。 数行後のコードでは、PagedDataSource
オブジェクトの DataSource に ProductDataTable
の DefaultView
が設定されます。
GetProductsSortedAsPagedDataSource メソッドを呼び出し、SortExpression 入力パラメーターの値を指定する
GetProductsSortedAsPagedDataSource
メソッドが完成したので、次はこのパラメーターに値を設定する必要があります。 SortingWithDefaultPaging.aspx
の ObjectDataSource は現在、GetProductsAsPagedDataSource
メソッドを呼び出すように構成されており、SelectParameters
コレクションで指定された 2 つの QueryStringParameters
を通じて 2 つの入力パラメーターを渡しています。 この 2 つの QueryStringParameters
は、GetProductsAsPagedDataSource
メソッドの pageIndex パラメーターと pageSize パラメーターのソースが、クエリ文字列フィールド pageIndex
と pageSize
から取得されることを示しています。
ObjectDataSource の SelectMethod
プロパティを更新して、新しい GetProductsSortedAsPagedDataSource
メソッドを呼び出すようにします。 次に、新しい QueryStringParameter
を追加して、sortExpression 入力パラメーターがクエリ文字列フィールド sortExpression
からアクセスされるようにします。 QueryStringParameter
の DefaultValue
を ProductName に設定します。
これらを変更すると、ObjectDataSource の宣言型マークアップは次のようになるはずです。
<asp:ObjectDataSource ID="ProductsDefaultPagingDataSource"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProductsSortedAsPagedDataSource"
OnSelected="ProductsDefaultPagingDataSource_Selected" runat="server">
<SelectParameters>
<asp:QueryStringParameter DefaultValue="ProductName"
Name="sortExpression" QueryStringField="sortExpression"
Type="String" />
<asp:QueryStringParameter DefaultValue="0" Name="pageIndex"
QueryStringField="pageIndex" Type="Int32" />
<asp:QueryStringParameter DefaultValue="4" Name="pageSize"
QueryStringField="pageSize" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
この時点で、SortingWithDefaultPaging.aspx
ページは結果を製品名のアルファベット順に並べ替えます (図 9 を参照)。 これは、既定では ProductName の値が GetProductsSortedAsPagedDataSource
メソッドの sortExpression パラメーターとして渡されるためです。
図 9: 既定では、結果は ProductName
別に並べ替えられる (クリックするとフルサイズの画像が表示されます)
sortExpression
クエリ文字列フィールド (SortingWithDefaultPaging.aspx?sortExpression=CategoryName
など) を手動で追加すると、指定された sortExpression
で結果が並べ替えられます。 ただし、このクエリ文字列パラメーター sortExpression
は、データの別のページに移動する際は含まれません。 実際、[次へ] や [最後] ページ ボタンをクリックすると、Paging.aspx
に戻ってしまいます。 さらに、現状では並べ替えのためのインターフェイスが用意されていません。 現状、ユーザーがページングされたデータの並べ替え順を変更するには、クエリ文字列を直接編集するしか方法がありません。
並べ替え用のインターフェイスを作成する
まず、RedirectUser
メソッドを更新して、ユーザーを Paging.aspx
ではなく SortingWithDefaultPaging.aspx
に送信し、sortExpression
値をクエリ文字列に含めるようにする必要があります。 また、読み取り専用のページレベルの名前付き SortExpression
プロパティも追加する必要があります。 このプロパティは、前のチュートリアルで作成した PageIndex
プロパティと PageSize
プロパティと同様、クエリ文字列に sortExpression
フィールドが存在する場合はその値を返し、存在しない場合は既定値 (ProductName) を返します。
現状、RedirectUser
メソッドが受け取るのは、表示するページのインデックスを指定する 1 つの入力パラメーターのみです。 ただし、クエリ文字列で指定されているものとは異なる並べ替え式を使用して、ユーザーをデータの特定のページにリダイレクトする必要が生じることもあります。 これから、このページの並べ替え用のインターフェイスを作成します。このインターフェイスには、指定された列でデータを並べ替えるための一連の Button Web コントロールが含まれます。 いずれかのボタンがクリックされたら、適切な並べ替え式の値を渡しながらユーザーをリダイレクトさせる必要があります。 この機能を提供するには、RedirectUser
メソッドを 2 つのバージョンで実装します。 1 つ目のバージョンは表示対象のページ インデックスのみを受け取るようにし、2 つ目のバージョンはページ インデックスと並べ替え式の両方を受け取るようにします。
private string SortExpression
{
get
{
if (!string.IsNullOrEmpty(Request.QueryString["sortExpression"]))
return Request.QueryString["sortExpression"];
else
return "ProductName";
}
}
private void RedirectUser(int sendUserToPageIndex)
{
// Use the SortExpression property to get the sort expression
// from the querystring
RedirectUser(sendUserToPageIndex, SortExpression);
}
private void RedirectUser(int sendUserToPageIndex, string sendUserSortingBy)
{
// Send the user to the requested page with the requested sort expression
Response.Redirect(string.Format(
"SortingWithDefaultPaging.aspx?pageIndex={0}&pageSize={1}&sortExpression={2}",
sendUserToPageIndex, PageSize, sendUserSortingBy));
}
このチュートリアルの最初の例では、DropDownList を使用して並べ替え用のインターフェイスを作成しました。 この例では、DataList の上に配置する 3 つの Button Web コントロールを使うことにします。それぞれ、ProductName
、CategoryName
、SupplierName
を基準に並べ替えるためのボタンです。 3 つの Button Web コントロールを追加し、それらの ID
プロパティと Text
プロパティを適切に設定します。
<p>
<asp:Button runat="server" id="SortByProductName"
Text="Sort by Product Name" />
<asp:Button runat="server" id="SortByCategoryName"
Text="Sort by Category" />
<asp:Button runat="server" id="SortBySupplierName"
Text="Sort by Supplier" />
</p>
次に、それぞれの Click
イベント ハンドラーを作成します。 これらのイベント ハンドラーでは、最初のページに戻るために、それぞれに対応する並べ替え式を使用して RedirectUser
メソッドを呼び出す必要があります。
protected void SortByProductName_Click(object sender, EventArgs e)
{
// Sort by ProductName
RedirectUser(0, "ProductName");
}
protected void SortByCategoryName_Click(object sender, EventArgs e)
{
// Sort by CategoryName
RedirectUser(0, "CategoryName");
}
protected void SortBySupplierName_Click(object sender, EventArgs e)
{
// Sort by SupplierName
RedirectUser(0, "SupplierName");
}
ページに最初にアクセスした際は、データは製品名のアルファベット順に並べ替えられています (図 9 を参照)。 [次へ] ボタンをクリックしてデータの 2 ページ目に進み、[カテゴリ別に並べ替え] ボタンをクリックします。 これにより、カテゴリ名で並べ替えられたデータの最初のページに戻ります (図 10 を参照)。 同様に、[仕入先別に並べ替え] ボタンをクリックすると、データの最初のページから仕入先別にデータが並べ替えられます。 データがページングされても、並べ替えの選択内容は保持されます。 図 11 は、カテゴリで並べ替えたあと、データの 13 ページ目に移動した際のページを示しています。
図 10: 製品がカテゴリ別に並べ替えられている (クリックするとフルサイズの画像が表示されます)
図 11: データのページング時に並べ替え式が保持される (クリックするとフルサイズの画像が表示されます)
手順 6: Repeater でのレコードのカスタム ページング
手順5 で確認した DataList の例では、非効率的な既定のページング手法を用いてデータのページング処理を行っています。 大量のデータをページングする場合には、カスタム ページングを使用することが不可欠となります。 大量のデータを効率的にページングするとカスタム ページングを適用したデータを並べ替えるに関するチュートリアルでは、既定のページングとカスタム ページングの違いを考察し、カスタム ページングの利用とカスタム ページングされたデータの並べ替えを実現するメソッドを BLL 内に実装しました。 具体的には、これら 2 つの前述のチュートリアルで、ProductsBLL
クラスに次の 3 つのメソッドを追加しました。
GetProductsPaged(startRowIndex, maximumRows)
は、startRowIndex から始まり、maximumRows を超えない特定のレコード サブセットを返します。GetProductsPagedAndSorted(sortExpression, startRowIndex, maximumRows)
は、指定された sortExpression 入力パラメーターで並べ替えられた特定のレコード サブセットを返します。TotalNumberOfProducts()
は、Products
データベース テーブル内のレコードの総数を返します。
これらのメソッドを使用すると、DataList や Repeater コントロールでデータを効率的にページングして並べ替えることができます。 このことを説明するために、まずカスタム ページング サポートを使用して Repeater コントロールを作成してみましょう。その後、並べ替え機能を追加します。
PagingSortingDataListRepeater
フォルダーの SortingWithCustomPaging.aspx
ページを開き、Repeater をページに追加して、その ID
プロパティを Products
に設定します。 Repeater のスマート タグから、ProductsDataSource
という名前の新しい ObjectDataSource を作成します。 ProductsBLL
クラスの GetProductsPaged
メソッドからデータを選択するように構成します。
図 12: ObjectDataSource を ProductsBLL
クラスの GetProductsPaged
メソッドを使用するように構成する (クリックするとフルサイズの画像が表示されます)
[挿入]、[更新]、[削除] の各タブのドロップダウン リストを [(なし)] に設定し、[次へ] ボタンをクリックします。 データ ソースの構成ウィザードで、GetProductsPaged
メソッドの startRowIndex 入力パラメーターと maximumRows 入力パラメーターのソースを入力するよう求められます。 実際には、これらの入力パラメーターは無視されます。 代わりに、startRowIndex と maximumRows の値は、このチュートリアルの最初のデモで sortExpression を指定したのと同様に、ObjectDataSource の Selecting
イベント ハンドラーの Arguments
プロパティを通じて渡されます。 したがって、ウィザードのパラメーター ソース ドロップダウン リストは None に設定したままにしておきます。
図 13: パラメーター ソースを [なし] に設定したままにする (クリックするとフルサイズの画像が表示されます)
Note
ObjectDataSource の EnablePaging
プロパティを true
に設定しない でください。 この設定を行うと、ObjectDataSource は独自の startRowIndex パラメーターと maximumRows パラメーターを SelectMethod
の既存のパラメーター リストに自動的に追加してしまいます。 EnablePaging
プロパティは、カスタム ページングされたデータを GridView、DetailsView、FormView コントロールにバインドする際に便利です。これらのコントロールは、ObjectDataSource に特定の動作を期待しますが、その動作が利用可能となるのは EnablePaging
プロパティが true
の場合のみだからです。 DataList と Repeater では、ページングのサポートを手動で追加する必要があるため、このプロパティは false
(既定値) に設定したままにしておきます。必要な機能は ASP.NET ページ内に直接組み込むためです。
最後に、製品の名前、カテゴリ、仕入先が表示されるように、Repeater の ItemTemplate
を定義します。 これらを変更すると、Repeater と ObjectDataSource の宣言型構文は次のようになるはずです。
<asp:Repeater ID="Products" runat="server" DataSourceID="ProductsDataSource"
EnableViewState="False">
<ItemTemplate>
<h4><asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>'></asp:Label></h4>
Category:
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Eval("CategoryName") %>'></asp:Label><br />
Supplier:
<asp:Label ID="SupplierNameLabel" runat="server"
Text='<%# Eval("SupplierName") %>'></asp:Label><br />
<br />
<br />
</ItemTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProductsPaged" TypeName="ProductsBLL">
<SelectParameters>
<asp:Parameter Name="startRowIndex" Type="Int32" />
<asp:Parameter Name="maximumRows" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
ブラウザーからページにアクセスし、レコードが返されないことを確認してください。 これは、startRowIndex パラメーター値と maximumRows パラメーター値をまだ指定していないので、両方のパラメーターに 0 の値が渡されているためです。 これらの値を指定するには、ObjectDataSource の Selecting
イベントのイベント ハンドラーを作成し、それぞれのパラメーター値をプログラムでハードコードされた値として 0 と 5 に設定します。
protected void ProductsDataSource_Selecting
(object sender, ObjectDataSourceSelectingEventArgs e)
{
e.InputParameters["startRowIndex"] = 0;
e.InputParameters["maximumRows"] = 5;
}
この変更により、ブラウザーでページを表示すると、最初の 5 つの製品が表示されるようになります。
図 14: 最初の 5 つのレコードが表示される (クリックするとフルサイズの画像が表示されます)
Note
図 14 に示された製品リストが製品名順に並んでいるのは、効率的なカスタム ページング クエリを実装した GetProductsPaged
ストアド プロシージャが、その結果を ProductName
によって並べ替えているためです。
ユーザーがページ間を移動できるようにするには、開始行インデックスと最大行数を追跡し、これらの値をポストバック間で保持しておく必要があります。 既定のページングの例では、これらの値を保持するためにクエリ文字列フィールドを使用しました。このデモでは、この情報をページのビュー ステートに保存しましょう。 次の 2 つのプロパティを作成します。
private int StartRowIndex
{
get
{
object o = ViewState["StartRowIndex"];
if (o == null)
return 0;
else
return (int)o;
}
set
{
ViewState["StartRowIndex"] = value;
}
}
private int MaximumRows
{
get
{
object o = ViewState["MaximumRows"];
if (o == null)
return 5;
else
return (int)o;
}
set
{
ViewState["MaximumRows"] = value;
}
}
次に、Selecting イベント ハンドラーのコードを更新して、ハードコードされた値 0 と 5 の代わりに StartRowIndex
プロパティと MaximumRows
プロパティを使用するようにします。
e.InputParameters["startRowIndex"] = StartRowIndex;
e.InputParameters["maximumRows"] = MaximumRows;
この時点では、ページにはまだ最初の 5 つのレコードしか表示されていません。 ただし、これらのプロパティを設定したことで、ページング インターフェイスを作成する準備が整いました。
ページング インターフェイスを追加する
現在表示されているデータのページ番号と全ページ数を表示する Label Web コントロールを含め、既定のページング例で使用したのと同じ [最初]、[前へ]、[次へ]、[最後] のページング用インターフェイスを使用してみましょう。 Repeater の下に 4 つの Button Web コントロールと Label を追加します。
<p>
<asp:Button runat="server" ID="FirstPage" Text="<< First" />
<asp:Button runat="server" ID="PrevPage" Text="< Prev" />
<asp:Button runat="server" ID="NextPage" Text="Next >" />
<asp:Button runat="server" ID="LastPage" Text="Last >>" />
</p>
<p>
<asp:Label runat="server" ID="CurrentPageNumber"></asp:Label>
</p>
次に、4 つのボタンの Click
イベント ハンドラーを作成します。 これらのボタンのいずれかがクリックされたら、StartRowIndex
を更新し、データを Repeater に再バインドする必要があります。 [最初]、[前へ]、[次へ] ボタンのコードは比較的単純ですが、[最後] ボタンについては、データの最後のページの開始行インデックスをどのように決定すればよいでしょうか。 このインデックスを計算し、[次へ] と [最後] ボタンを有効にするかどうかを判断できるようにするには、ページングされているレコードの合計数を把握する必要があります。 これは、ProductsBLL
クラスの TotalNumberOfProducts()
メソッドを呼び出すことで判断できます。 TotalNumberOfProducts()
メソッドの結果を返す、TotalRowCount
という名前の読み取り専用のページ レベルのプロパティを作ってみましょう。
private int TotalRowCount
{
get
{
// Return the value from the TotalNumberOfProducts() method
ProductsBLL productsAPI = new ProductsBLL();
return productsAPI.TotalNumberOfProducts();
}
}
このプロパティを使用すると、最後のページの開始行インデックスを決定できるようになります。 具体的には、TotalRowCount
から 1 を引いた値を MaximumRows
で割り、MaximumRows
を掛けた整数の結果になります。 これで、ページング インターフェイスの 4 つのボタンの Click
イベント ハンドラーを記述できます。
protected void FirstPage_Click(object sender, EventArgs e)
{
// Return to StartRowIndex of 0 and rebind data
StartRowIndex = 0;
Products.DataBind();
}
protected void PrevPage_Click(object sender, EventArgs e)
{
// Subtract MaximumRows from StartRowIndex and rebind data
StartRowIndex -= MaximumRows;
Products.DataBind();
}
protected void NextPage_Click(object sender, EventArgs e)
{
// Add MaximumRows to StartRowIndex and rebind data
StartRowIndex += MaximumRows;
Products.DataBind();
}
protected void LastPage_Click(object sender, EventArgs e)
{
// Set StartRowIndex = to last page's starting row index and rebind data
StartRowIndex = ((TotalRowCount - 1) / MaximumRows) * MaximumRows;
Products.DataBind();
}
最後に、データの最初のページを表示している場合はページング インターフェイスの [最初] と [前へ] ボタンを無効にし、最後のページを表示している場合は [次へ] と [最後] ボタンを無効にする必要があります。 この機能を実装するには、ObjectDataSource の Selecting
イベント ハンドラーに次のコードを追加します。
// Disable the paging interface buttons, if needed
FirstPage.Enabled = StartRowIndex != 0;
PrevPage.Enabled = StartRowIndex != 0;
int LastPageStartRowIndex = ((TotalRowCount - 1) / MaximumRows) * MaximumRows;
NextPage.Enabled = StartRowIndex < LastPageStartRowIndex;
LastPage.Enabled = StartRowIndex < LastPageStartRowIndex;
これらの Click
イベント ハンドラーと、現在の開始行インデックスに基づいてページング インターフェイス要素を有効化または無効化するコードを追加したら、ブラウザーでページをテストします。 図 15 に示すように、初めてこのページにアクセスすると、[最初] ボタンと [前へ] ボタンが無効になっています。 [次へ] をクリックするとデータの 2 ページ目が表示され、[最後] をクリックすると最後のページが表示されます (図 16 と 17 を参照)。 データの最後のページを表示している際は、[次へ] ボタンと [最後] ボタンの両方が無効になっています。
図 15: 製品の最初のページを表示しているときは [前へ] と [最後] ボタンが無効になる (クリックするとフルサイズの画像が表示されます)
図 16: 製品の 2 ページ目が表示される (クリックするとフルサイズの画像が表示されます)
図 17: [最後] をクリックするとデータの最後のページが表示される (クリックするとフルサイズの画像が表示されます)
手順 7: カスタム ページングされた Repeater に並べ替えサポートを含める
カスタム ページングが実装されたので、並べ替えサポートを追加する準備が整いました。 ProductsBLL
クラスの GetProductsPagedAndSorted
メソッドには、GetProductsPaged
と同じ startRowIndex 入力パラメーターと maximumRows 入力パラメーターがありますが、さらに sortExpression という追加の入力パラメーターを指定できます。 SortingWithCustomPaging.aspx
から GetProductsPagedAndSorted
メソッドを使用するには、次の手順を実行する必要があります。
- ObjectDataSource の
SelectMethod
プロパティをGetProductsPaged
からGetProductsPagedAndSorted
に変更します。 - ObjectDataSource の
SelectParameters
コレクションに sortExpressionParameter
オブジェクトを追加します。 - ポストバック時にも値を保持できるように、ページのビュー ステートを利用したプライベートな
SortExpression
プロパティをページレベルで作成します。 - ObjectDataSource の
Selecting
イベント ハンドラーを更新し、ObjectDataSource の sortExpression パラメーターにページ レベルのSortExpression
プロパティの値を設定します。 - 並べ替え用のインターフェイスを作成します。
まず、ObjectDataSource の SelectMethod
プロパティを更新し、 sortExpression Parameter
を追加します。 sortExpression Parameter
のType
プロパティがString
に設定されていることを確認します。 最初の 2 つのタスクを完了すると、ObjectDataSource の宣言型マークアップは次のようになります。
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProductsPagedAndSorted"
OnSelecting="ProductsDataSource_Selecting">
<SelectParameters>
<asp:Parameter Name="sortExpression" Type="String" />
<asp:Parameter Name="startRowIndex" Type="Int32" />
<asp:Parameter Name="maximumRows" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
次に、値がビュー ステートにシリアル化されるページ レベルの SortExpression
プロパティが必要です。 並べ替え式の値が設定されていない場合は、ProductName を既定値として使用します。
private string SortExpression
{
get
{
object o = ViewState["SortExpression"];
if (o == null)
return "ProductName";
else
return o.ToString();
}
set
{
ViewState["SortExpression"] = value;
}
}
ObjectDataSource が GetProductsPagedAndSorted
メソッドを呼び出す前に、 sortExpression Parameter
SortExpression
プロパティの値を設定する必要があります。 Selecting
イベント ハンドラーに、次のコード行を追加します。
e.InputParameters["sortExpression"] = SortExpression;
あとは並べ替え用のインターフェイスを実装するだけです。 前例と同様に、製品名、カテゴリ、または仕入先を基準に結果を並べ替えることができる 3 つの Button Web コントロールを使用して、並べ替え用のインターフェイスを実装してみましょう。
<asp:Button runat="server" id="SortByProductName"
Text="Sort by Product Name" />
<asp:Button runat="server" id="SortByCategoryName"
Text="Sort by Category" />
<asp:Button runat="server" id="SortBySupplierName"
Text="Sort by Supplier" />
この 3 つの Button コントロールの Click
イベント ハンドラーを作成します。 イベント ハンドラーでは、StartRowIndex
を 0 にリセットし、SortExpression
を適切な値に設定して、データを Repeater に再バインドします。
protected void SortByProductName_Click(object sender, EventArgs e)
{
StartRowIndex = 0;
SortExpression = "ProductName";
Products.DataBind();
}
protected void SortByCategoryName_Click(object sender, EventArgs e)
{
StartRowIndex = 0;
SortExpression = "CategoryName";
Products.DataBind();
}
protected void SortBySupplierName_Click(object sender, EventArgs e)
{
StartRowIndex = 0;
SortExpression = "CompanyName";
Products.DataBind();
}
以上で終了です。 カスタム ページングと並べ替えの実装には多くの手順が必要でしたが、これらの手順は既定のページングに必要な手順とほぼ同じでした。 図 18 は、カテゴリ別に並べ替えられたデータの最後のページに表示される製品を示しています。
図 18: カテゴリ別に並べ替えられたデータの最後のページが表示される (クリックするとフルサイズの画像が表示されます)
Note
前の例では、仕入先で並べ替える場合、SupplierName が並べ替え式として使用されました。 ただし、カスタム ページングの実装では "CompanyName" を使う必要があります。 これは、カスタム ページングを実装する GetProductsPagedAndSorted
ストアド プロシージャが並べ替え式を ROW_NUMBER()
キーワードに渡すためです。ROW_NUMBER()
キーワードには、エイリアスではなく実際のカラム名を指定する必要があります。 したがって、並べ替え式には、SELECT
クエリ (SupplierName
) で使われているエイリアスではなく、Suppliers
テーブルのカラム名 (CompanyName
) を使用する必要があります。
まとめ
DataList も Repeater も組み込みの並べ替えサポートは提供していませんが、少しコードを記述してカスタムの並べ替え用のインターフェイスを使用することで、そのような機能を追加できます。 並べ替えを実装する一方でページング機能は実装しない場合、並べ替え式は、ObjectDataSource の Select
メソッドに渡される DataSourceSelectArguments
オブジェクトから指定できます。 この DataSourceSelectArguments
オブジェクトの SortExpression
プロパティは、ObjectDataSource の Selecting
イベント ハンドラーで設定できます。
すでにページング サポートを提供している DataList や Repeater に並べ替え機能を追加する最も簡単な方法は、ビジネス ロジック レイヤーをカスタマイズして、並べ替え式を受け取るメソッドを含める方法です。 この情報は、ObjectDataSource の SelectParameters
のパラメーターとして渡すことができます。
このチュートリアルで、DataList と Repeater コントロールを使用したページングと並べ替えの説明を終了します。 次回の最終チュートリアルでは、DataList と Repeater のテンプレートに Button Web コントロールを追加して、アイテムごとにユーザーが実行できるカスタム機能を提供する方法について説明します。
プログラミングに満足!
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は David Suru でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。