Share via


2 つの DropDownList でマスター/詳細をフィルター処理する (C#)

作成者: Scott Mitchell

PDF のダウンロード

このチュートリアルでは、マスター/詳細リレーションシップを拡大して 3 番目のレイヤーを追加し、2 つの DropDownList コントロールを使用して、目的の親および親の親レコードを選択します。

はじめに

前のチュートリアルでは、カテゴリが設定された 1 つの DropDownList と、選択したカテゴリに属する製品を表示する GridView を使用して、単純なマスター/詳細レポートを表示する方法を調べました。 このレポート パターンは、1 対多リレーションシップを含むレコードを表示する場合に適しており、複数の 1 対多リレーションシップを含むシナリオで機能するように簡単に拡張できます。 たとえば、注文入力システムには、顧客、注文、および注文の品目に対応するテーブルがあります。 特定の顧客が、各注文が複数の品目で構成される、複数の注文を行っている場合があります。 このようなデータは、2 つの DropDownList と 1 つの GridView を使用してユーザーに表示できます。 1 つ目の DropDownList にはデータベース内の各顧客のリスト項目があり、2 つ目の内容は、選択した顧客によって行われた注文です。 GridView では、選択した注文の品目が一覧表示されます。

Northwind データベースには、その CustomersOrdersOrder Details テーブル内に正規の顧客/注文/注文の詳細の情報が含まれていますが、これらのテーブルはこのアーキテクチャにはキャプチャされません。 それでも、2 つの依存する DropDownList の使用について説明することはできます。 1 つ目の DropDownList にはカテゴリが、2 つ目には選択したカテゴリに属する第 2 の製品が、それぞれ一覧表示されます。 これで、選択された製品の詳細が DetailsView に一覧表示されます。

手順 1: カテゴリの DropDownList を作成して内容を設定する

最初のゴールは、カテゴリを一覧表示する DropDownList を追加することです。 これらの手順については前のチュートリアルで詳しく検討しましたが、ここでは要約を示して、全体がわかるようにします。

Filtering フォルダー内の MasterDetailsDetails.aspx ページを開き、そのページに DropDownList を追加し、その ID プロパティを Categories に設定し、次に、そのスマート タグの [データ ソースの構成] リンクをクリックします。 [Data Source Configuration Wizard] (データ ソース構成ウィザード) で、新しいデータ ソースを選択して追加します。

Add a New Data Source for the DropDownList

図 1: DropDownList の新しいデータ ソースを追加する (クリックするとフルサイズの画像が表示されます)

新しいデータ ソースは当然、ObjectDataSource です。 この新しい ObjectDataSource に CategoriesDataSource という名前を付け、それによって CategoriesBLL オブジェクトの GetCategories() メソッドが呼び出されるようにします。

Choose to Use the CategoriesBLL Class

図 2: CategoriesBLL クラスを選択して使用する (クリックするとフルサイズの画像が表示されます)

Configure the ObjectDataSource to Use the GetCategories() Method

図 3: GetCategories() メソッドを使用するように ObjectDataSource を構成する (クリックするとフルサイズの画像が表示されます)

ObjectDataSource を構成した後も、Categories DropDownList にどのデータ ソース フィールドを表示するか、およびリスト項目の値としてどれを構成するかを指定する必要があります。 各リスト項目の表示として CategoryName フィールドを、値として CategoryID を設定します。

Have the DropDownList Display the CategoryName Field and Use CategoryID as the Value

図 4: DropDownList に CategoryName フィールドを表示し、値として CategoryID を使用する (クリックするとフルサイズの画像が表示されます)

この時点で、Categories テーブルのレコードが入力された DropDownList コントロール (Categories) があります。 ユーザーが DropDownList から新しいカテゴリを選択すると、手順 2 で作成する製品の DropDownList を更新するためにポストバックが実行される必要が生じます。 そのため、categories DropDownList のスマート タグから [AutoPostBack を有効にする] オプションをオンにします。

Enable AutoPostBack for the Categories DropDownList

図 5: Categories DropDownList の [AutoPostBack を有効にする] (クリックするとフルサイズの画像が表示されます)

手順 2: 2 番目の DropDownList で選択したカテゴリの製品を表示する

Categories DropDownList が完了したら、次の手順では、選択したカテゴリに属する製品の DropDownList を表示します。 これを実現するには、別の DropDownList を ProductsByCategory という名前のページに追加します。 Categories DropDownList と同様に、ProductsByCategoryDataSource という名前の ProductsByCategory DropDownList の新しい ObjectDataSource を作成します。

Add a New Data Source for the ProductsByCategory DropDownList

図 6: ProductsByCategory DropDownList の新しいデータ ソースを追加する (クリックするとフルサイズの画像が表示されます)

Create a New ObjectDataSource Named ProductsByCategoryDataSource

図 7: ProductsByCategoryDataSource という名前の新しい ObjectDataSource を作成する (クリックするとフルサイズの画像が表示されます)

ProductsByCategory DropDownList では、選択したカテゴリに属する製品のみを表示する必要があるため、ObjectDataSource で ProductsBLL オブジェクトから GetProductsByCategoryID(categoryID) メソッドを呼び出します。

Screenshot of the Configure Data Source - productsByCategoryDataSource window with ProductsBLL selected and the Next button highlighted.

図 8: ProductsBLL クラスを選択して使用する (クリックするとフルサイズの画像が表示されます)

Configure the ObjectDataSource to Use the GetProductsByCategoryID(categoryID) Method

図 9: GetProductsByCategoryID(categoryID) メソッドを使用するように ObjectDataSource を構成する (クリックするとフルサイズの画像が表示されます)

ウィザードの最後の手順では、categoryID パラメーターの値を指定する必要があります。 このパラメーターを Categories DropDownList の選択された項目に割り当てます。

Pull the categoryID Parameter Value from the Categories DropDownList

図 10: categoryID パラメーターの値を Categories DropDownList からプルする (クリックするとフルサイズの画像が表示されます)

ObjectDataSource が構成されたら、残すは、DropDownList の項目の表示と値に使用されるデータ ソース フィールドを指定することだけです。 ProductName フィールドを表示し、値として ProductID フィールドを使用します。

Specify the Data Source Fields Used for the DropDownList's ListItems' Text and Value Properties

図 11: DropDownList の ListItemText および Value プロパティに使用するデータ ソース フィールドを指定する (クリックするとフルサイズの画像が表示されます)

ObjectDataSource と ProductsByCategory DropDownList が構成されたら、ページには 2 つの DropDownList が表示されます。1 つ目にはすべてのカテゴリが一覧表示され、2 つ目には選択したカテゴリに属する製品が一覧表示されます。 ユーザーが 1 つ目の DropDownList から新しいカテゴリを選択すると、ポストバックが実行され、2 つ目の DropDownList が再バインドされ、新しく選択されたカテゴリに属する製品が表示されます。 図 12 と 13 は、ブラウザーで表示した場合の MasterDetailsDetails.aspx の様子を示しています。

When First Visiting the Page, the Beverages Category is Selected

図 12: 最初にページにアクセスすると、[Beverages] (飲料) カテゴリが選択される (クリックするとフルサイズの画像が表示されます)

Choosing a Different Category Displays the New Category's Products

図 13: 別のカテゴリを選択すると、新しいカテゴリの製品が表示される (クリックするとフルサイズの画像が表示されます)

現在、productsByCategory DropDownList が変更された場合、ポストバックは "行われません"。 ただし、選択した製品の詳細を表示するために DetailsView を追加したときは、ポストバックが行われる必要があります (手順 3)。 そのため、productsByCategory DropDownList のスマート タグから [AutoPostBack を有効にする] チェックボックスをオンにします。

Enable the AutoPostBack Feature for the productsByCategory DropDownList

図 14: productsByCategory DropDownList の AutoPostBack 機能を有効にする (クリックするとフルサイズの画像が表示されます)

手順 3: DetailsView を使用して選択した製品の詳細を表示する

最後の手順では、選択した製品の詳細を DetailsView に表示します。 これを実現するには、DetailsView をページに追加し、その ID プロパティを ProductDetails に設定して、そのための新しい ObjectDataSource を作成します。 productID パラメーターの値として ProductsByCategory DropDownList の選択した値を使用して、ProductsBLL クラスの GetProductByProductID(productID) メソッドからデータをプルするように、この ObjectDataSource を構成します。

Screenshot of the Configure Data Source - productsByCategoryDataSource window where ProductsBLL is selected and the Next button is highlighted.

図 15: ProductsBLL クラスを選択して使用する (クリックするとフルサイズの画像が表示されます)

Configure the ObjectDataSource to Use the GetProductByProductID(productID) Method

図 16: GetProductByProductID(productID) メソッドを使用するように ObjectDataSource を構成する (クリックするとフルサイズの画像が表示されます)

Pull the productID Parameter Value from the ProductsByCategory DropDownList

図 17: productID パラメーターの値を ProductsByCategory DropDownList からプルする (クリックするとフルサイズの画像が表示されます)

DetailsView で使用可能な任意のフィールドを選択して表示できます。 ProductIDSupplierIDCategoryID フィールドを削除し、残りのフィールドを並べ替えて書式設定することにしました。 さらに、DetailsView の Height および Width プロパティをクリアして、指定したサイズに制限するのではなく、データを最適に表示するために必要な幅に DetailsView を拡張できるようにしました。 マークアップ全体は以下のように表示されます。

<asp:DetailsView ID="ProductDetails" runat="server"
    AutoGenerateRows="False" DataKeyNames="ProductID"
    DataSourceID="ObjectDataSource1" EnableViewState="False">
    <Fields>
        <asp:BoundField DataField="ProductName"
          HeaderText="Product" SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryName"
          HeaderText="Category" ReadOnly="True"
          SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName"
          HeaderText="Supplier" ReadOnly="True"
          SortExpression="SupplierName" />
        <asp:BoundField DataField="QuantityPerUnit"
          HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" />
        <asp:BoundField DataField="UnitPrice"
          DataFormatString="{0:c}" HeaderText="Price"
          HtmlEncode="False" SortExpression="UnitPrice" />
        <asp:BoundField DataField="UnitsInStock"
          HeaderText="UnitsInStock" SortExpression="Units In Stock" />
        <asp:BoundField DataField="UnitsOnOrder"
          HeaderText="UnitsOnOrder" SortExpression="Units On Order" />
        <asp:BoundField DataField="ReorderLevel"
          HeaderText="ReorderLevel" SortExpression="Reorder Level" />
        <asp:CheckBoxField DataField="Discontinued"
          HeaderText="Discontinued" SortExpression="Discontinued" />
    </Fields>
</asp:DetailsView>

時間をとって、ブラウザーで MasterDetailsDetails.aspx ページを表示してみてください。 一見すると、すべてが希望どおりに動作しているように見えますが、わずかな問題があります。 新しいカテゴリを選択すると、ProductsByCategory DropDownList は選択したカテゴリの製品を含むように更新されますが、ProductDetails DetailsView には引き続き以前の製品情報が表示されます。 選択したカテゴリで別の製品を選択すると、DetailsView が更新されます。 さらに、十分にテストした場合、新しいカテゴリを続けて選択する (たとえば、Categories DropDownList からまず [Beverages] (飲料) を、次に [Condiments] (調味料)、[Confections] (菓子) の順に選択する) と、他のすべてのカテゴリの選択では ProductDetails DetailsView が更新されることがわかります。

この問題を明確化するために、具体的な例を見てみましょう。 最初にこのページにアクセスすると、[Beverages] (飲料) カテゴリが選択され、関連する製品が ProductsByCategory DropDownList に読み込まれます。 [Chai] (チャイ) が選択された製品であり、図 18 に示すように ProductDetails DetailsView にその詳細が表示されます。

The Selected Product's Details are Displayed in a DetailsView

図 18: 選択された製品の詳細が DetailsView に表示される (クリックするとフルサイズの画像が表示されます)

カテゴリの選択を [Beverages] (飲料) から [Condiments] (調味料) に変更すると、ポストバックが行われ、それに応じて ProductsByCategory DropDownList が更新されますが、DetailsView には引き続き [Chai] (チャイ) の詳細が表示されます。

The Previously Selected Product's Details are Still Displayed

図 19: 前に選択した製品の詳細が引き続き表示される (クリックするとフルサイズの画像が表示されます)

一覧から新しい製品を選択すると、DetailsView が想定どおりに更新されます。 製品を変更した後で新しいカテゴリを選択すると、この場合も DetailsView は更新されません。 ただし、新しい製品を選択する代わりに新しいカテゴリを選択した場合、DetailsView は更新されます。 ここでは、いったい何が起こっているのでしょうか?

問題は、ページのライフサイクルにおけるタイミングの問題です。 ページが要求されると、そのつど、いくつもの手順を経てそれがレンダリングされます。 これらの手順のいずれかにおいて、ObjectDataSource コントロールで、SelectParameters 値のいずれかが変更されたかどうかが確認されます。 その場合、ObjectDataSource にバインドされたデータ Web コントロールでは、その表示を更新する必要があることを認識します。 たとえば、新しいカテゴリが選択されると、ProductsByCategoryDataSource ObjectDataSource では、そのパラメーター値が変更されたことを検出し、ProductsByCategory DropDownList では自らを再バインドし、選択されたカテゴリの製品を取得します。

この状況で発生する問題は、ページのライフサイクル中において、変更されたパラメーターがあるかどうかが ObjectDataSources で確認される時点が、関連するデータ Web コントロールの再バインドより "前" になることです。 そのため、新しいカテゴリが選択されると、ProductsByCategoryDataSource ObjectDataSource では、そのパラメーターの値の変更を検出します。 ただし、ProductDetails DetailsView によって使用される ObjectDataSource では、こうした変更は認識されません。ProductsByCategory DropDownList がまだ再バインドされていないためです。 ProductsByCategory DropDownList では、ライフサイクルの後の方でその ObjectDataSource に再バインドし、新しく選択されたカテゴリの製品を取得します。 ProductsByCategory DropDownList の値が変更されたとき、ProductDetails DetailsView の ObjectDataSource では既にパラメーター値の確認を行っているため、DetailsView には前の結果が表示されます。 この相互作用を図 20 に示します。

The ProductsByCategory DropDownList Value Changes After the ProductDetails DetailsView's ObjectDataSource Checks for Changes

図 20: ProductDetails DetailsView の ObjectDataSource で変更が確認された後に ProductsByCategory DropDownList の値が変更された (クリックするとフルサイズの画像が表示されます)

これに対処するには、ProductsByCategory DropDownList がバインドされた後に ProductDetails DetailsView を明示的に再バインドする必要があります。 これを実現するには、ProductsByCategoryDropDownList の DataBound イベントが発生したときに ProductDetails DetailsView の DataBind() メソッドを呼び出します。 MasterDetailsDetails.aspx ページの分離コード クラスに次のイベント ハンドラー コードを追加します (イベント ハンドラーの追加方法の説明については、「ObjectDataSource のパラメーター値をプログラムで設定する」を参照してください)。

protected void ProductsByCategory_DataBound(object sender, EventArgs e)
{
    ProductDetails.DataBind();
}

ProductDetails DetailsView の DataBind() メソッドに対するこの明示的な呼び出しが追加されたら、チュートリアルは想定どおりに進みます。 図 21 は、この変更で以前の問題がどのように改善されたかを強調しています。

The ProductDetails DetailsView is Explicitly Refreshed When the ProductsByCategory DropDownList's DataBound Event Fires

図 21: ProductsByCategory DropDownList の DataBound イベントが発生したときに ProductDetails DetailsView が明示的に更新される (クリックするとフルサイズの画像が表示されます)

まとめ

DropDownList は、マスターと詳細のレコードの間に 1 対多のリレーションシップがある、マスター/詳細レポートの理想的なユーザー インターフェイス要素として機能します。 前のチュートリアルでは、1 つの DropDownList を使用して、選択されたカテゴリによって表示される製品をフィルター処理する方法について確認しました。 このチュートリアルでは、製品の GridView を DropDownList に置き換え、DetailsView を使用して選択された製品の詳細を表示しました。 このチュートリアルで説明する概念は、顧客、注文、注文の品目など、複数の 1 対多リレーションシップを含むデータ モデルに簡単に拡張できます。 一般に、1 対多リレーションシップのそれぞれの "1" エンティティに対し、常に DropDownList を追加できます。

プログラミングに満足!

著者について

Scott Mitchell は、ASP/ASP.NET に関する 7 冊の書籍の著者であり、4GuysFromRolla.com の設立者でもあります。1998 年以降、Microsoft の Web テクノロジを活用した業務を行っています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は Hilton Giesenow でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。