Repeater コントロールと DataList を使って 2 つのページでマスター/詳細をフィルター処理する (VB)
このチュートリアルでは、マスター/詳細レポートを 2 つのページに分ける方法について説明します。 "マスター" ページでは、Repeater コントロールを使ってカテゴリの一覧をレンダリングします。これをクリックすると、ユーザーは "詳細" ページに移動し、選択したカテゴリに属する製品が 2 列の DataList に表示されます。
はじめに
2 つのページでマスター/詳細をフィルター処理する チュートリアルでは、GridView を使ってシステム内のすべてのサプライヤーを表示することで、このパターンを確認しました。 この GridView には、HyperLinkField が含まれていました。これは 2 番目のページへのリンクとしてレンダリングされ、クエリ文字列で SupplierID
を渡します。 2 番目のページでは、GridView を使って、選択したサプライヤーが提供する製品を一覧表示しました。
このような 2 ページのマスター/詳細レポートは、DataList コントロールと Repeater コントロールを使って実現することもできます。 唯一の違いは、DataList も Repeater も HyperLinkField コントロールをサポートしていないということです。 代わりに、HyperLink Web コントロールか HTML のアンカー要素 (<a>
) をコントロールの ItemTemplate
内に追加する必要があります。 そして、HyperLink の NavigateUrl
プロパティまたはアンカーの href
属性を、宣言による方法かプログラムによる方法でカスタマイズできます。
このチュートリアルでは、各カテゴリを Repeater コントロールを使って 1 つのページに箇条書きで一覧表示する例について説明します。 一覧の各項目はそのカテゴリの名前と説明を含み、カテゴリ名は 2 番目のページへのリンクとして表示されます。 このリンクをクリックすると、ユーザーは 2 番目のページに移動し、選択したカテゴリに属する製品が DataList に表示されます。
手順 1: カテゴリを箇条書きで表示する
マスター/詳細レポートを作成する最初の手順は、まず "マスター" レコードを表示することです。 そのため、最初のタスクは "マスター" ページにカテゴリを表示することです。 DataListRepeaterFiltering
フォルダー内の CategoryListMaster.aspx
ページを開き、Repeater コントロールを追加して、スマート タグから新しい ObjectDataSource の追加を選択します。 CategoriesBLL
クラスの GetCategories
メソッドからデータにアクセスするように新しい ObjectDataSource を構成します (図 1 を参照)。
図 1: CategoriesBLL
クラスの GetCategories
メソッドを使用するように ObjectDataSource を構成する (フルサイズの画像を表示するにはクリック)
次に、各カテゴリ名と説明を箇条書きの項目として表示するように Repeater のテンプレートを定義します。 各カテゴリを詳細ページにリンクする方法については、まだ心配しなくて構いません。 Repeater と ObjectDataSource の宣言型マークアップを次に示します。
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1"
EnableViewState="False">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><%# Eval("CategoryName") %> - <%# Eval("Description") %></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
このマークアップが完成したら、ブラウザーで進行状況を確認しましょう。 図 2 に示すように、Repeater は箇条書きでレンダリングされ、各カテゴリの名前と説明が表示されます。
図 2: 各カテゴリが箇条書きの項目として表示されている (フルサイズの画像を表示するにはクリック)
手順 2: カテゴリ名を詳細ページへのリンクにする
ユーザーが特定のカテゴリの "詳細" 情報を表示できるようにするために、各箇条書き項目にリンクを追加する必要があります。このリンクをクリックすると、ユーザーは 2 番目のページ (ProductsForCategoryDetails.aspx
) に移動します。 そして、この 2 番目のページで、DataList を使って選択したカテゴリの製品を表示します。 クリックされたリンクのカテゴリを特定するには、何らかのメカニズムを使ってクリックされたカテゴリの CategoryID
を 2 番目のページに渡す必要があります。 あるページから別のページにスカラー データを転送する最もシンプルでわかりやすい方法は、クエリ文字列を使うことです。このチュートリアルではこのオプションを使います。 具体的には、 ProductsForCategoryDetails.aspx
ページで、選択した categoryID
値が CategoryID
という名前のクエリ文字列フィールドを介して渡されることを想定します。 たとえば、 CategoryID
が 1 の Beverages カテゴリの製品を表示する場合、ユーザーは ProductsForCategoryDetails.aspx?CategoryID=1
にアクセスします。
Repeater の各箇条書き項目にハイパーリンクを作成するには、HyperLink Web コントロールか HTML のアンカー要素 (<a>
) を ItemTemplate
に追加する必要があります。 各行で同じようにハイパーリンクを表示するシナリオでは、どちらの方法でも構いません。 Repeater の場合、筆者はアンカー要素を使う方が好きです。 アンカー要素を使う場合、Repeater の ItemTemplate を次のように更新します。
<li>
<a href='ProductsForCategoryDetails.aspx?CategoryID=<%# Eval("CategoryID") %>'>
<%# Eval("CategoryName") %>
</a> - <%# Eval("Description") %>
</li>
アンカー要素の href
属性内に CategoryID
を直接挿入できることに注意してください。ただしそのためには、 href
属性の値を必ずアポストロフィで区切る必要があります (引用符ではありません)。 href
属性内の Eval
メソッドで、その文字列 ("CategoryID"
) を引用符で区切っているためです。 代わりに、HyperLink Web コントロールを使うこともできます。
<li>
<asp:HyperLink runat="server" Text='<%# Eval("CategoryName") %>'
NavigateUrl='<%# "ProductsForCategoryDetails.aspx?CategoryID=" &
Eval("CategoryID") %>'>
</asp:HyperLink>
- <%# Eval("Description") %>
</li>
データ バインド構文内で文字列連結を使って、URL の静的部分 (ProductsForCategoryDetails.aspx?CategoryID
) を Eval("CategoryID")
の結果に直接追加していることに注意してください。
HyperLink コントロールを使う利点の 1 つは、必要に応じて、Repeater の ItemDataBound
イベント ハンドラーからプログラムでアクセスできる点です。 たとえば、関連付けられている製品がないカテゴリについては、カテゴリ名をリンクではなくテキストとして表示したい場合があります。 このようなチェックは、 ItemDataBound
イベント ハンドラー内でプログラムによって実行できます。関連付けられている製品がないカテゴリは、HyperLink の NavigateUrl
プロパティを空の文字列に設定することで、その特定のカテゴリ名を (リンクではなく) プレーンテキストとしてレンダリングすることができます。 ItemDataBound
イベント ハンドラーを使ってプログラムのロジックに基づいて DataList と Repeater の内容を書式設定する方法について詳しくは、「データに基づいて DataList と Repeater を書式設定する」チュートリアルをもう一度参照してください。
先に進むにあたっては、ページでアンカー要素と HyperLink コントロールのどちらの方法を使っても構いません。 どちらの方法でも、ブラウザーを介してページを表示すると、各カテゴリ名が ProductsForCategoryDetails.aspx
へのリンクとしてレンダリングされ、適切な CategoryID
値が渡されるはずです (図 3 を参照)。
図 3: ProductsForCategoryDetails.aspx
にリンクされるようになったカテゴリ名 (フルサイズの画像を表示するにはクリック)
手順 3: 選択したカテゴリに属する製品を一覧表示する
CategoryListMaster.aspx
ページが完成したら、次は "詳細" ページ (ProductsForCategoryDetails.aspx
) の実装に注意を向けましょう。 このページを開き、ツールボックスからデザイナーに DataList をドラッグして、その ID
プロパティを ProductsInCategory
に設定します。 次に、DataList のスマート タグから、新しい ObjectDataSource をページに追加することを選択し、 ProductsInCategoryDataSource
という名前を付けます。 ProductsBLL
クラスの GetProductsByCategoryID(categoryID)
メソッドを呼び出すようにそれを構成します。[INSERT]、[UPDATE]、[DELETE] タブのドロップダウン リストは [(なし)] に設定します。
図 4: ProductsBLL
クラスの GetProductsByCategoryID(categoryID)
メソッドを使用するように ObjectDataSource を構成する (フルサイズの画像を表示するにはクリック)
GetProductsByCategoryID(categoryID)
メソッドは入力パラメーター (categoryID
) を受け取るので、[データ ソースの選択] ウィザードではパラメーターのソースを指定できます。 パラメーターのソースを QueryString に設定し、QueryStringField CategoryID
を使います。
図 5: パラメーターのソースとして Querystring フィールド CategoryID
を使用する (フルサイズの画像を表示するにはクリック)
前のチュートリアルで説明したように、[データ ソースの選択] ウィザードを完了すると、Visual Studio によって自動的に、各データ フィールドの名前と値を一覧表示する DataList の ItemTemplate
が作成されます。 このテンプレートを、製品の名前、サプライヤー、価格のみを一覧表示するテンプレートに置き換えます。 また、DataList の RepeatColumns
プロパティを 2 に設定します。 これらの変更後、DataList と ObjectDataSource の宣言型マークアップは次のようになります。
<asp:DataList ID="ProductsInCategory" DataKeyField="ProductID" RepeatColumns="2"
DataSourceID="ProductsInCategoryDataSource" EnableViewState="False"
runat="server">
<ItemTemplate>
<h5><%# Eval("ProductName") %></h5>
<p>
Supplied by <%# Eval("SupplierName") %><br />
<%# Eval("UnitPrice", "{0:C}") %>
</p>
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="ProductsInCategoryDataSource"
OldValuesParameterFormatString="original_{0}" runat="server"
SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
<SelectParameters>
<asp:QueryStringParameter Name="categoryID" QueryStringField="CategoryID"
Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
このページの動作を確認するには、 CategoryListMaster.aspx
ページから開始して、カテゴリの箇条書きのリンクをクリックします。 そうすると、 ProductsForCategoryDetails.aspx
に移動し、クエリ文字列によって CategoryID
が渡されます。 次に、 ProductsForCategoryDetails.aspx
の ProductsInCategoryDataSource
ObjectDataSource が指定したカテゴリの製品のみを取得し、DataList で表示します。それは行ごとに 2 つの製品をレンダリングします。 図 6 は、Beverages を表示したときの ProductsForCategoryDetails.aspx
のスクリーンショットを示しています。
図 6: Beverages の表示 (行ごとに 2 つ) (フルサイズの画像を表示するにはクリック)
手順 4: ProductsForCategoryDetails.aspx でカテゴリ情報を表示する
ユーザーが CategoryListMaster.aspx
でカテゴリをクリックすると、 ProductsForCategoryDetails.aspx
に移動して、選択したカテゴリに属する製品が表示されます。 しかし ProductsForCategoryDetails.aspx
には、どのカテゴリが選択されたかについての視覚的な手掛かりがありません。 ユーザーが Beverages をクリックするつもりで間違って Condiments をクリックしてしまった場合、 ProductsForCategoryDetails.aspx
に移動した後にその間違いに気付く方法はありません。 この潜在的な問題を軽減するために、選択したカテゴリに関する情報 (その名前と説明) を ProductsForCategoryDetails.aspx
ページの上部に表示できます。
これを実現するには、 ProductsForCategoryDetails.aspx
の Repeater コントロールの上に FormView を追加します。 次に、FormView のスマート タグから CategoryDataSource
という名前の新しい ObjectDataSource をページに追加し、 CategoriesBLL
クラスの GetCategoryByCategoryID(categoryID)
メソッドを使用するように構成します。
図 7: CategoriesBLL
クラスの GetCategoryByCategoryID(categoryID)
メソッドを使ってカテゴリに関する情報にアクセスする (フルサイズの画像を表示するにはクリック)
手順 3 で追加した ProductsInCategoryDataSource
ObjectDataSource と同様に、 CategoryDataSource
の [データ ソースの構成] ウィザードでは GetCategoryByCategoryID(categoryID)
メソッドの入力パラメーターのソースを求められます。 以前とまったく同じ設定を使って、パラメーターのソースを QueryString に設定し、QueryStringField の値を CategoryID
に設定します (前の図 5 を参照)。
ウィザードが完了すると、Visual Studio によって自動的に FormView の ItemTemplate
、 EditItemTemplate
および InsertItemTemplate
が作成されます。 読み取り専用のインターフェイスを表示するので、 EditItemTemplate
と InsertItemTemplate
は削除して構いません。 また、FormView の ItemTemplate
も自由にカスタマイズしてください。 余分なテンプレートを削除して ItemTemplate をカスタマイズした後、FormView と ObjectDataSource の宣言型マークアップは次のようになります。
<asp:FormView ID="FormView1" runat="server" DataKeyNames="CategoryID"
DataSourceID="CategoryDataSource" EnableViewState="False" Width="100%">
<ItemTemplate>
<h3>
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Bind("CategoryName") %>' />
</h3>
<p>
<asp:Label ID="DescriptionLabel" runat="server"
Text='<%# Bind("Description") %>' />
</p>
</ItemTemplate>
</asp:FormView>
<asp:ObjectDataSource ID="CategoryDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategoryByCategoryID" TypeName="CategoriesBLL">
<SelectParameters>
<asp:QueryStringParameter Name="categoryID" Type="Int32"
QueryStringField="CategoryID" />
</SelectParameters>
</asp:ObjectDataSource>
図 8 は、ブラウザーでこのページを表示したときのスクリーンショットを示しています。
Note
筆者は FormView に加えて、ユーザーをカテゴリの一覧 (CategoryListMaster.aspx
) に戻す HyperLink コントロールを FormView の上に追加しました。 このリンクは他の場所に配置しても、または完全に省略しても構いません。
図 8: ページ上部にカテゴリ情報が表示されるようになった (フルサイズの画像を表示するにはクリック)
手順 5: 選択したカテゴリに属する製品がない場合にメッセージを表示する
CategoryListMaster.aspx
ページでは、関連付けられている製品があるかどうかに関係なく、システム内のすべてのカテゴリが一覧表示されます。 ユーザーが関連付けられた製品のないカテゴリをクリックした場合、 ProductsForCategoryDetails.aspx
の DataList はレンダリングされません。そのデータ ソースには項目がないためです。 前のチュートリアルで説明したように、GridView には EmptyDataText
プロパティが用意されており、これを使って、データ ソースにレコードがない場合に表示するテキスト メッセージを指定できます。 残念ながら、DataList と Repeater のどちらにもこのようなプロパティはありません。
選択したカテゴリに一致する製品がないことをユーザーに伝えるメッセージを表示するには、ページに Label コントロールを追加し、一致する製品がないときに表示するメッセージをその Text
プロパティに割り当てる必要があります。 その後、DataList に項目が含まれているかどうかに基づいて、その Visible
プロパティをプログラムで設定する必要があります。
これを実現するには、まず DataList の下に Label を追加します。 その ID
プロパティを NoProductsMessage
に、 Text
プロパティを "There are no products for the selected category…" (選択したカテゴリの製品がありません…) に設定します。次に、 ProductsInCategory
DataList にデータがバインドされたかどうかに基づいて、この Label の Visible
プロパティをプログラムで設定する必要があります。 この割り当ては、データが DataList にバインドされた後に行う必要があります。 GridView、DetailsView、FormView の場合は、データ バインドが完了した後に発生するコントロールの DataBound
イベントに対して、イベント ハンドラーを作成することができました。 しかし、DataList と Repeater では、どちらも DataBound
イベントを使用できません。
この特定の例では、 Page_Load
イベント ハンドラーで Label の Visible
プロパティを割り当てることができます。ページの Load
イベントより前にデータが DataList に割り当てられるためです。 ただし、ObjectDataSource のデータはページのライフサイクルにおいて後ほど DataList にバインドされる可能性があるため、この方法は一般的なケースでは機能しません。 たとえば、表示されるデータが別のコントロールの値に基づいている場合 ("マスター" レコードを保持するために DropDownList を使ってマスター/詳細レポートを表示する場合など)、データはページのライフ サイクルの PreRender
ステージまで、データの Web コントロールに再バインドされない可能性があります。
すべてのケースで機能する 1 つの解決策は、 Item
または AlternatingItem
のアイテムの種類をバインドするときに、DataList の ItemDataBound
(または ItemCreated
) イベント ハンドラーで Visible
プロパティを False
に割り当てることです。 このような場合は、データ ソースに少なくとも 1 つのデータ項目があることがわかるため、 NoProductsMessage
ラベルを非表示にすることができます。 このイベント ハンドラーに加えて、DataList の DataBinding
イベントに対するイベント ハンドラーも必要です。そこで Label の Visible
プロパティを True
に初期化します。 DataBinding
イベントは ItemDataBound
イベントの前に発生するため、Label の Visible
プロパティは最初は True
に設定されますが、データ項目がある場合は False
に設定されます。 このロジックを実装するコードを次に示します。
Protected Sub ProductsInCategory_DataBinding(sender As Object, e As EventArgs) _
Handles ProductsInCategory.DataBinding
'Show the Label
NoProductsMessage.Visible = True
End Sub
Protected Sub ProductsInCategory_ItemDataBound(s As Object, e As DataListItemEventArgs) _
Handles ProductsInCategory.ItemDataBound
'If we have a data item, hide the Label
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = _
ListItemType.AlternatingItem Then
NoProductsMessage.Visible = False
End If
End Sub
Northwind データベース内のすべてのカテゴリは、1 つ以上の製品に関連付けられています。 この機能をテストするために、筆者はこのチュートリアルの Northwind データベースを手動で調整し、Produce カテゴリ (CategoryID
= 7) に関連付けられているすべての製品を Seafood カテゴリ (CategoryID
= 8) に再割り当てしました。 これを実行するには、サーバー エクスプローラーから [新しいクエリ] を選択して、次の UPDATE
ステートメントを使用します。
UPDATE Products SET
CategoryID = 8
WHERE CategoryID = 7
データベースを適切に更新した後、 CategoryListMaster.aspx
ページに戻り、[Produce] リンクをクリックします。 Produce カテゴリに属する製品はもうないため、図 9 のように "There are no products for the selected category…" というメッセージが表示されるはずです。
図 9: 選択したカテゴリに属する製品がない場合にメッセージが表示される (フルサイズの画像を表示するにはクリック)
まとめ
マスター/詳細レポートでは、マスター レコードと詳細レコードの両方を 1 つのページに表示できますが、多くの Web サイトでは 2 つの Web ページに分かれています。 このチュートリアルでは、"マスター" Web ページで Repeater を使ってカテゴリを箇条書きで一覧表示し、"詳細" ページで関連付けられた製品を表示することで、このようなマスター/詳細レポートを実装する方法について説明しました。 マスター Web ページの一覧の各項目には詳細ページへのリンクが含まれており、その行の CategoryID
値が渡されました。
指定されたサプライヤーの製品を取得する詳細ページは、 ProductsBLL
クラスの GetProductsByCategoryID(categoryID)
メソッドを使って実現しました。 categoryID
パラメーターの値は、 CategoryID
クエリ文字列値をパラメーター ソースとして使って宣言によって指定しました。 また、FormView を使って詳細ページにカテゴリの詳細を表示する方法と、選択したカテゴリに属する製品がない場合にメッセージを表示する方法についても説明しました。
プログラミングに満足!
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.comの創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の著書は Sams Teach Yourself ASP.NET 2.0 in 24 Hoursです。 彼に連絡するには、 mitchell@4GuysFromRolla.com. または彼のブログ (http://ScottOnWriting.NETにあります) にアクセスしてください。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Zack Jones と Liz Shulok でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 mitchell@4GuysFromRolla.comに行をドロップしてください。