GridView にボタンを追加し、ボタンに応答する (C#)

作成者: Scott Mitchell

PDF のダウンロード

このチュートリアルでは、テンプレートに対してと GridView または DetailsView コントロールのフィールドに対してカスタム ボタンを追加する方法について説明します。 具体的には、FormView (ユーザーがサプライヤーごとのページを表示できる) があるインターフェイスを構築します。

はじめに

多くのレポート シナリオにはレポート データへの読み取り専用アクセスが含まれますが、レポートに、表示されるデータに基づいてアクションを実行する機能を含めることは珍しくありません。 一般的に、このためには、レポート内に表示される各レコードに Button、LinkButton、または ImageButton Web コントロールを追加し、クリックするとポストバックが発生してサーバー側のコードが呼び出されるようにします。 レコードごとのデータの編集と削除が最も一般的な例です。 実際に、「データの挿入、更新、削除の概要」チュートリアルから説明してきたように、編集と削除は一般的であり、コードを 1 行も記述せずに GridView、DetailsView、FormView コントロールでこのような機能をサポートできます。

GridView、DetailsView、FormView コントロールには、編集および削除ボタンに加え、クリックするとカスタムのサーバー側ロジックが実行される Button、LinkButton、ImageButton を含めることもできます。 このチュートリアルでは、テンプレートに対してと GridView または DetailsView コントロールのフィールドに対してカスタム ボタンを追加する方法について説明します。 具体的には、FormView (ユーザーがサプライヤーごとのページを表示できる) があるインターフェイスを構築します。 指定されたサプライヤーについて、FormView に、そのサプライヤーに関する情報と、クリックするとその関連するすべての製品が廃止としてマークされる Button Web コントロールが表示されます。 また、GridView に、選択したサプライヤーが提供している製品が一覧表示され、各行に値上げおよび値下げボタン (クリックするとその製品の UnitPrice が 10% ずつ値上げまたは値下げされる) が表示されます (図 1 を参照)。

FormView と GridView の両方に、カスタム アクションを実行するボタンが含まれている

図 1: FormView と GridView の両方に、カスタム アクションを実行するボタンが含まれている (クリックするとフルサイズの画像が表示されます)

手順 1: ボタン チュートリアルの Web ページを追加する

カスタム ボタンを追加する方法を説明する前に、まず、このチュートリアルに必要な ASP.NET ページを Web サイト プロジェクトで作成しましょう。 まず、CustomButtons という名前の新しいフォルダーを追加します。 次に、そのフォルダーに次の 2 つの ASP.NET ページを追加し、必ず各ページを Site.master マスター ページに関連付けます:

  • Default.aspx
  • CustomButtons.aspx

カスタム ボタン関連のチュートリアルの ASP.NET ページを追加する

図 2: カスタム ボタン関連のチュートリアルの ASP.NET ページを追加する

他のフォルダーと同様に、CustomButtons フォルダーの Default.aspx のセクションにチュートリアルが一覧表示されます。 SectionLevelTutorialListing.ascx ユーザー コントロールでこの機能が提供されていることを思い出してください。 そのため、ソリューション エクスプローラーからページのデザイン ビューにドラッグして、このユーザー コントロールを Default.aspx に追加します。

SectionLevelTutorialListing.ascx ユーザー コントロールを Default.aspx に追加する

図 3: SectionLevelTutorialListing.ascx ユーザー コントロールを Default.aspx に追加する (フルサイズの画像を表示する場合はこちらをクリック)

最後に、このページをエントリとして Web.sitemap ファイルに追加します。 具体的には、ページングと並べ替えの <siteMapNode> の後に次のマークアップを追加します:

<siteMapNode
    title="Adding Custom Buttons"
    description="Samples of Reports that Include Buttons for Performing
                  Server-Side Actions"
    url="~/CustomButtons/Default.aspx">
    <siteMapNode
        title="Using ButtonFields and Buttons in Templates"
        description="Examines how to add custom Buttons, LinkButtons,
                      or ImageButtons as ButtonFields or within templates."
        url="~/CustomButtons/CustomButtons.aspx" />
</siteMapNode>

Web.sitemap を更新した後、ブラウザーでチュートリアル Web サイトの表示を確認してみましょう。 左側のメニューに、チュートリアルの編集、挿入、削除の項目が含まれるようになりました。

サイト マップにカスタム ボタン チュートリアルのエントリが含まれている

図 4: サイト マップにカスタム ボタン チュートリアルのエントリが含まれるようになった

手順 2: サプライヤーを一覧表示する FormView を追加する

このチュートリアルでは、まず、サプライヤーを一覧表示する FormView を追加しましょう。 「はじめに」で説明したように、この FormView では、ユーザーがサプライヤーごとのページを表示でき、そのサプライヤーが提供している製品が GridView で表示されます。 また、この FormView には、クリックするとすべてのサプライヤーの製品が廃止としてマークされるボタンが含まれます。 FormView へのカスタム ボタンの追加に取り掛かる前に、まず、サプライヤー情報が表示されるように FormView を作成しましょう。

まず、CustomButtons フォルダーの CustomButtons.aspx ページを開きます。 FormView をツールボックスからデザイナーにドラッグしてページに追加し、その ID プロパティを Suppliers に設定します。 FormView のスマート タグで、SuppliersDataSource という名前の新しい ObjectDataSource を作成することにします。

SuppliersDataSource という名前の新しい ObjectDataSource を作成する

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

SuppliersBLL クラスの GetSuppliers() メソッドからクエリを実行するように、この新しい ObjectDataSource を構成します (図 6 を参照)。 この FormView ではサプライヤー情報を更新するためのインターフェイスを提供しないため、[UPDATE] タブ内のドロップダウン リストから [(なし)] オプションを選択します。

SuppliersBLL クラスの GetSuppliers() メソッドを使用するようにデータ ソースを構成する

図 6: SuppliersBLL クラスの GetSuppliers() メソッドを使うようにデータ ソースを構成する (クリックするとフルサイズの画像が表示されます)

ObjectDataSource を構成すると、Visual Studio で、その FormView 用の InsertItemTemplateEditItemTemplateItemTemplate が生成されます。 InsertItemTemplateEditItemTemplate を削除し、サプライヤーの会社名と電話番号のみが表示されるように ItemTemplate を変更します。 最後に、FormView でのページングのサポートを、そのスマート タグで [ページングを有効にする] チェック ボックスを選択して (または、その AllowPaging プロパティを True に設定して) 有効にします。 これらの変更の後、GridView の宣言型マークアップは次のようになります:

<asp:FormView ID="Suppliers" runat="server" DataKeyNames="SupplierID"
    DataSourceID="SuppliersDataSource" EnableViewState="False" AllowPaging="True">
    <ItemTemplate>
        <h3>
            <asp:Label ID="CompanyName" runat="server"
                Text='<%# Bind("CompanyName") %>' />
        </h3>
        <b>Phone:</b>
        <asp:Label ID="PhoneLabel" runat="server" Text='<%# Bind("Phone") %>' />
    </ItemTemplate>
</asp:FormView>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>

図 7 に、ブラウザーで表示したときの CustomButtons.aspx ページを示します。

現在選択されているサプライヤーの CompanyName フィールドと Phone フィールドが FormView に表示される

図 7: 現在選択しているサプライヤーの CompanyName および Phone フィールドが FormView で表示される (クリックするとフルサイズの画像が表示されます)

手順 3: 選択されたサプライヤーの製品を一覧表示する GridView を追加する

FormView のテンプレートに [Discontinue All Products] (すべての製品を廃止) ボタンを追加する前に、まず、選択されたサプライヤーから提供されている製品を一覧表示する GridView を FormView の下に追加します。 これを実現するには、GridView をページに追加し、その ID プロパティを SuppliersProducts に設定し、SuppliersProductsDataSource という名前の新しい ObjectDataSource を追加します。

SuppliersProductsDataSource という名前の新しい ObjectDataSource を作成する

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

ProductsBLL クラスの GetProductsBySupplierID(supplierID) メソッドを使うようにこの ObjectDataSource を構成します (図 9 を参照)。 この GridView では製品の価格を調整できますが、GridView に組み込まれている編集および削除機能は使いません。 そのため、ObjectDataSource の [UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを [(なし)] に設定します。

ProductsBLL クラスの GetProductsBySupplierID(supplierID) メソッドを使用するようにデータ ソースを構成する

図 9: ProductsBLL クラスの GetProductsBySupplierID(supplierID) メソッドを使うようにデータ ソースを構成する (クリックするとフルサイズの画像が表示されます)

GetProductsBySupplierID(supplierID) メソッドは入力パラメーターを受け取るため、ObjectDataSource ウィザードで、このパラメーター値のソースを入力するよう求められます。 FormView から SupplierID 値を渡すには、[パラメーター ソース] ドロップダウン リストを [コントロール] に、[ControlID] ドロップダウン リストを Suppliers (手順 2 で作成した FormView の ID) に設定します。

supplierID パラメーターが Suppliers という FormView コントロールから取得されることを指定する

図 10: supplierID パラメーターを Suppliers FormView コントロールから取得する必要があることを示す (クリックするとフルサイズの画像が表示されます)

ObjectDataSource ウィザードを完了すると、GridView に、製品の各データ フィールドの BoundField または CheckBoxField が含まれます。 これを減らして、ProductName および UnitPrice BoundField と Discontinued CheckBoxField のみが表示されるようにしましょう。また、テキストが通貨として書式設定されるように UnitPrice BoundField を書式設定しましょう。 GridView と SuppliersProductsDataSource ObjectDataSource の宣言型マークアップは、次のマークアップのようになります:

<asp:GridView ID="SuppliersProducts" AutoGenerateColumns="False"
    DataKeyNames="ProductID" DataSourceID="SuppliersProductsDataSource"
    EnableViewState="False" runat="server">
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" HeaderText="Price"
            SortExpression="UnitPrice" DataFormatString="{0:C}"
            HtmlEncode="False" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="SuppliersProductsDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProductsBySupplierID" TypeName="ProductsBLL">
    <SelectParameters>
        <asp:ControlParameter ControlID="Suppliers" Name="supplierID"
            PropertyName="SelectedValue" Type="Int32" />
    </SelectParameters>
</asp:ObjectDataSource>

このチュートリアルでは、この時点で、マスター/詳細レポートが表示され、ユーザーが上部の FormView でサプライヤーを選択でき、そのサプライヤーから提供されている製品を下部の GridView で表示できるようになります。 図 11 に、FormView からサプライヤー Tokyo Traders を選択したときのこのページのスクリーン ショットを示します。

選択されているサプライヤーの製品が GridView に表示される

図 11: 選択したサプライヤーの製品が GridView に表示される (クリックするとフルサイズの画像が表示されます)

手順 4: サプライヤーのすべての製品を廃止にするための DAL および BLL メソッドを作成する

クリックするとそのサプライヤーの製品すべてが廃止になるボタンを FormView に追加する前に、まず、このアクションを実行するメソッドを DAL と BLL に追加する必要があります。 具体的に述べると、このメソッドには DiscontinueAllProductsForSupplier(supplierID) という名前を付けます。 FormView のボタンをクリックすると、ビジネス ロジック層 (BLL) でこのメソッドが呼び出されて、選択したサプライヤーの SupplierID が渡されます。その後、BLL で、対応するデータ アクセス層 (DAL) のメソッドが呼び出されます。これにより、指定されたサプライヤーの製品を廃止にする UPDATE ステートメントがデータベースに対して発行されます。

前のチュートリアルと同様に、ボトムアップ アプローチを使い、まず DAL メソッドを作成し、次に BLL メソッドを作成し、最後に ASP.NET ページで機能を実装します。 App_Code/DAL フォルダー内の型指定された DataSet Northwind.xsd を開き、新しいメソッドを ProductsTableAdapter に追加します (ProductsTableAdapter を右クリックし、[クエリの追加] を選択する)。 これにより、新しいメソッドを追加するプロセスを示す TableAdapter クエリ構成ウィザードが起動されます。 まず、DAL メソッドでアドホック SQL ステートメントを使うことを指定します。

アドホック SQL ステートメントを使用して DAL メソッドを作成する

図 12: アドホック SQL ステートメントを使って DAL メソッドを作成する (クリックするとフルサイズの画像が表示されます)

次に、ウィザードで、作成するクエリの種類を確認されます。 DiscontinueAllProductsForSupplier(supplierID) メソッドでは、Products データベース テーブルを更新して、指定された supplierID で提供されているすべての製品について Discontinued フィールドを 1 に設定する必要があります。そのため、データを更新するクエリを作成する必要があります。

UPDATE というクエリの種類を選択する

図 13: クエリの種類として UPDATE を選択する (クリックするとフルサイズの画像が表示されます)

次のウィザード画面では、Products DataTable で定義されている各フィールドを更新する、TableAdapter の既存の UPDATE ステートメントが提供されます。 このクエリ テキストを次のステートメントに置き換えます:

UPDATE [Products] SET
   Discontinued = 1
WHERE SupplierID = @SupplierID

このクエリを入力し、[次へ] をクリックすると、最後のウィザード画面で、その新しいメソッドの名前に DiscontinueAllProductsForSupplier を使うかどうかを確認されます。 [完了] ボタンをクリックしてウィザードを完了します。 データセット デザイナーに戻ると、ProductsTableAdapter 内に DiscontinueAllProductsForSupplier(@SupplierID) という名前の新しいメソッドがあります。

新しい DAL メソッドに DiscontinueAllProductsForSupplier という名前を付ける

図 14: 新しい DAL メソッドに DiscontinueAllProductsForSupplier という名前を付ける (クリックするとフルサイズの画像が表示されます)

データ アクセス層で DiscontinueAllProductsForSupplier(supplierID) メソッドを作成したので、次のタスクは、ビジネス ロジック層での DiscontinueAllProductsForSupplier(supplierID) メソッドの作成です。 これを実現するには、ProductsBLL クラス ファイルを開き、次の内容を追加します:

public int DiscontinueAllProductsForSupplier(int supplierID)
{
    return Adapter.DiscontinueAllProductsForSupplier(supplierID);
}

このメソッドでは、DAL 内の DiscontinueAllProductsForSupplier(supplierID) メソッドが呼び出されて、指定した supplierID パラメーター値が渡されるだけです。 特定の状況下でのみサプライヤーの製品を廃止できるようにするビジネス ルールがあった場合は、BLL において、ここでそれらのルールを実装する必要があります。

Note

ProductsBLL クラス内の UpdateProduct オーバーロードとは異なり、DiscontinueAllProductsForSupplier(supplierID) メソッドのシグネチャには DataObjectMethodAttribute 属性 (<System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, Boolean)>) は含まれません。 これにより、ObjectDataSource のデータ ソース構成ウィザードの [UPDATE] タブにあるドロップダウン リストから DiscontinueAllProductsForSupplier(supplierID) メソッドが除外されます。この ASP.NET ページではイベント ハンドラーから DiscontinueAllProductsForSupplier(supplierID) メソッドを直接呼び出すため、この属性を省略しました。

手順 5: FormView に [Discontinue All Products] (すべての製品を廃止) ボタンを追加する

BLL と DAL での DiscontinueAllProductsForSupplier(supplierID) メソッドが完成したので、選択されたサプライヤーのすべての製品を廃止にする機能を追加するための最後の手順は、FormView の ItemTemplate への Button Web コントロールの追加です。 そのようなボタンを、ボタン テキストを [Discontinue All Products] (すべての製品を廃止) にし ID プロパティの値を DiscontinueAllProductsForSupplier にしてサプライヤーの電話番号の下に追加しましょう。 この Button Web コントロールは、デザイナーで FormView のスマート タグ (図 15 を参照) にある [テンプレートの編集] リンクをクリックして追加するか、宣言構文で直接追加することができます。

図 15: FormView の ItemTemplate に [Discontinue All Products] (すべての製品を廃止) Button Web コントロールを追加する (クリックするとフルサイズの画像が表示されます)

ページにアクセスしているユーザーがこのボタンをクリックすると、ポストバックが発生し、FormView の ItemCommand イベントが発生します。 このボタンのクリックに応答してカスタム コードを実行するには、このイベントのイベント ハンドラーを作成します。 ただし、FormView 内で Button、LinkButton、ImageButton Web コントロールのどれかがクリックされるたびに ItemCommand イベントが発生することを理解しておいてください。 つまり、ユーザーが FormView 内のあるページから別のページに移動すると ItemCommand イベントが発生するということです。挿入、更新、または削除をサポートする FormView 内で [New] (新規)、[Edit] (編集)、または [Delete] (削除) をクリックしたときも同様です。

クリックされたボタンに関係なく ItemCommand が発生するため、イベント ハンドラーでは、クリックされたのが [Discontinue All Products] (すべての製品を廃止) ボタンだったか他のボタンだったかを判断する方法が必要です。 これを実現するために、Button Web コントロールの CommandName プロパティを特定の値に設定できます。 ボタンがクリックされると、この CommandName 値が ItemCommand イベント ハンドラーに渡されます。それにより、クリックされたのが [Discontinue All Products] (すべての製品を廃止) ボタンであるかどうかを判断できるようになります。 [Discontinue All Products] (すべての製品を廃止) ボタンの CommandName プロパティを DiscontinueProducts に設定します。

最後に、クライアント側の確認ダイアログ ボックスを使って、選択したサプライヤーの製品を本当に廃止にするかどうかをユーザーに確認します。 「削除時、クライアント側の確認を追加する」チュートリアルで説明したように、これは少しの JavaScript で実現できます。 具体的には、Button Web コントロールの OnClientClick プロパティを return confirm('This will mark _all_ of this supplier\'s products as discontinued. Are you certain you want to do this?'); に設定します

これらの変更を加えた後の、FormView の宣言構文は次のようになります:

<asp:FormView ID="Suppliers" runat="server" DataKeyNames="SupplierID"
    DataSourceID="SuppliersDataSource" EnableViewState="False"
    AllowPaging="True">
    <ItemTemplate>
        <h3><asp:Label ID="CompanyName" runat="server"
            Text='<%# Bind("CompanyName") %>'></asp:Label></h3>
        <b>Phone:</b>
        <asp:Label ID="PhoneLabel" runat="server" Text='<%# Bind("Phone") %>' />
        <br />
        <asp:Button ID="DiscontinueAllProductsForSupplier" runat="server"
            CommandName="DiscontinueProducts" Text="Discontinue All Products"
            OnClientClick="return confirm('This will mark _all_ of this supplier\'s
                products as discontinued. Are you certain you want to do this?');" />
    </ItemTemplate>
</asp:FormView>

次に、FormView の ItemCommand イベントのイベント ハンドラーを作成します。 このイベント ハンドラーでは、まず、[Discontinue All Products] (すべての製品を廃止) ボタンがクリックされたかどうかを判断する必要があります。 その場合は、ProductsBLL クラスのインスタンスを作成し、その DiscontinueAllProductsForSupplier(supplierID) メソッドを呼び出して、選択された FormView の SupplierID を渡します:

protected void Suppliers_ItemCommand(object sender, FormViewCommandEventArgs e)
{
    if (e.CommandName.CompareTo("DiscontinueProducts") == 0)
    {
        // The "Discontinue All Products" Button was clicked.
        // Invoke the ProductsBLL.DiscontinueAllProductsForSupplier(supplierID) method
        // First, get the SupplierID selected in the FormView
        int supplierID = (int)Suppliers.SelectedValue;
        // Next, create an instance of the ProductsBLL class
        ProductsBLL productInfo = new ProductsBLL();
        // Finally, invoke the DiscontinueAllProductsForSupplier(supplierID) method
        productInfo.DiscontinueAllProductsForSupplier(supplierID);
    }
}

FormView 内の現在選択されているサプライヤーの SupplierID に FormView の SelectedValue プロパティを使ってアクセスできることに注目してください。 SelectedValue プロパティでは、FormView で表示されているレコードの最初のデータ キー値が返されます。 FormView の DataKeyNames プロパティ (データ キー値の取得元のデータ フィールドを示す) は、手順 2 で ObjectDataSource を FormView にバインドしたときに、Visual Studio によって自動的に SupplierID に設定されています。

ItemCommand イベント ハンドラーを作成したので、このページをテストします。 サプライヤー Cooperativa de Quesos 'Las Cabras' (FormView 内の 5 番目のサプライヤー) を参照します。 このサプライヤーは、Queso Cabrales と Queso Manchego La Pastora という 2 つの製品を提供しており、それらは両方とも廃止になっていません。

たとえば、Cooperativa de Quesos 'Las Cabras' が廃業したためその製品を廃止にするとします。 [Discontinue All Products] (すべての製品を廃止) ボタンをクリックします。 これにより、クライアント側の確認ダイアログ ボックスが表示されます (図 16 を参照)。

Cooperativa de Quesos Las Cabras から 2 つのアクティブな製品が供給されている

図 16: Cooperativa de Quesos Las Cabras はアクティブな製品を 2 つ供給している (クリックするとフルサイズの画像が表示されます)

クライアント側の確認ダイアログ ボックスで [OK] をクリックすると、フォームの送信が開始されて、ポストバックが発生し、それにより FormView の ItemCommand イベントが発生します。 次に、作成したイベント ハンドラーが実行されて、DiscontinueAllProductsForSupplier(supplierID) メソッドが呼び出され、Queso Cabrales と Queso Manchego La Pastora という製品両方が廃止になります。

GridView のビュー状態を無効にしてある場合、GridView は、ポストバックのたびに、基になるデータ ストアに再バインドされます。それにより、直ちに更新されて、これら 2 つの製品が廃止になったことが反映されます (図 17 を参照)。 しかし、GridView でビュー状態を無効にしていない場合は、この変更を加えた後で、手動でデータを GridView に再バインドする必要があります。 これを実現するには、DiscontinueAllProductsForSupplier(supplierID) メソッドを呼び出した直後に GridView の DataBind() メソッドを呼び出します。

[すべての製品を廃止] ボタンをクリックするとそれに応じてサプライヤーの製品が更新される

図 17: [Discontinue All Products] (すべての製品を廃止) ボタンをクリックするとそれに応じてサプライヤーの製品が更新される (クリックするとフルサイズの画像が表示されます)

手順 6: ビジネス ロジック層に製品価格調整用の UpdateProduct オーバーロードを作成する

FormView 内の [Discontinue All Products] (すべての製品を廃止) ボタンと同様に、製品の価格を増減するためのボタンを GridView に追加するには、まず、データ アクセス層 (DAL) とビジネス ロジック層 (BLL) の適切なメソッドを追加する必要があります。 DAL 内に 1 つの製品行を更新するメソッドが既に存在するため、BLL 内の UpdateProduct メソッドに新しいオーバーロードを作成すると、このような機能を提供できます。

過去の UpdateProduct のオーバーロードでは、製品フィールドの組み合わせがスカラー入力値として取得され、その後、指定した製品のフィールドのみが更新されました。 このオーバーロードの場合は、この標準と少し異なり、代わりに製品の ProductID と、UnitPrice を調整する割合を渡します (調整した新しい UnitPrice 自体を渡すのではない)。 この方法では、現在の製品の UnitPrice を特定する必要がないため、ASP.NET ページの分離コード クラスで記述する必要があるコードが簡単になります。

このチュートリアルの UpdateProduct オーバーロードを次に示します:

public bool UpdateProduct(decimal unitPriceAdjustmentPercentage, int productID)
{
    Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
    if (products.Count == 0)
        // no matching record found, return false
        return false;
    Northwind.ProductsRow product = products[0];
    // Adjust the UnitPrice by the specified percentage (if it's not NULL)
    if (!product.IsUnitPriceNull())
        product.UnitPrice *= unitPriceAdjustmentPercentage;
    // Update the product record
    int rowsAffected = Adapter.Update(product);
    // Return true if precisely one row was updated, otherwise false
    return rowsAffected == 1;
}

このオーバーロードでは、DAL の GetProductByProductID(productID) メソッドで、指定された製品について情報が取得されます。 次に、製品の UnitPrice にデータベースの NULL 値が割り当てられているかどうかが確認されます。 そうであれば、価格はそのままになります。 ただし、NULL UnitPrice以外の値がある場合、メソッドは指定されたパーセント (unitPriceAdjustmentPercent) で製品のUnitPriceを更新します。

手順 7: GridView に増減ボタンを追加する

GridView (および DetailsView) は、どちらもフィールドのコレクションから成ります。 ASP.NET には、BoundField、CheckBoxField、TemplateField に加え、ButtonField が含まれています。これは、その名前が示しているように、各行に Button、LinkButton、または ImageButton がある列としてレンダリングされます。 FormView と同様に、GridView のページング ボタン、編集または削除ボタン、並べ替えボタンなどのボタンをクリックすると、ポストバックが発生し、GridView の RowCommand イベントが発生します。

ButtonField には、指定された値をそのボタンの CommandName プロパティにそれぞれ割り当てる CommandName プロパティがあります。 FormView と同様に、CommandName 値は、RowCommand イベント ハンドラーで、どのボタンがクリックされたかを判断するために使われます。

2 つの新しい ButtonField を GridView に追加しましょう。一方はボタン テキストが [Price +10%] (価格 +10%) で、もう一方は [Price -10%] (価格 -10%) です。 これらの ButtonField を追加するには、GridView のスマート タグから [列の編集] リンクをクリックし、左上のリストからフィールドの種類として ButtonField を選択し、[追加] ボタンをクリックします。

GridView に 2 つの ButtonField を追加する

図 18: GridView に 2 つの ButtonField を追加する

それら 2 つの ButtonField を移動して、最初の 2 つの GridView フィールドとして表示されるようにします。 次に、これら 2 つの ButtonField の Text プロパティを Price +10% と Price -10% に、CommandName プロパティをそれぞれ IncreasePrice と DecreasePrice に設定します。 既定では、ButtonField ではボタンの列が LinkButton としてレンダリングされます。 ただし、これは ButtonField の ButtonType プロパティを使って変更できます。 これら 2 つの ButtonField を通常のプッシュ ボタンとしてレンダリングされるようにしましょう。そのために、ButtonType プロパティを Button に設定します。 図 19 で、これらの変更を加えた後の [フィールド] ダイアログ ボックスを示し、その後に、GridView の宣言型マークアップを示します。

ButtonField の Text、CommandName、ButtonType の各プロパティを構成する

図 19: ButtonField の TextCommandNameButtonType プロパティを構成する

<asp:GridView ID="SuppliersProducts" runat="server" AutoGenerateColumns="False"
    DataKeyNames="ProductID" DataSourceID="SuppliersProductsDataSource"
    EnableViewState="False">
    <Columns>
        <asp:ButtonField ButtonType="Button" CommandName="IncreasePrice"
            Text="Price +10%" />
        <asp:ButtonField ButtonType="Button" CommandName="DecreasePrice"
            Text="Price -10%" />
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" HeaderText="Price"
            SortExpression="UnitPrice" DataFormatString="{0:C}"
            HtmlEncode="False" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>

これらの ButtonField を作成したので、最後の手順は、GridView の RowCommand イベントのイベント ハンドラーの作成です。 このイベント ハンドラーでは、[Price +10%] (価格 +10%) または [Price -10%] (価格 -10%) ボタンがクリックされて発生した場合は、ボタンがクリックされた行の ProductID を特定してから、ProductsBLL クラスの UpdateProduct メソッドを呼び出し、適切な UnitPrice 調整割合と共に ProductID を渡す必要があります。 次のコードでこれらのタスクが実行されます:

protected void SuppliersProducts_RowCommand(object sender, GridViewCommandEventArgs e)
{
    if (e.CommandName.CompareTo("IncreasePrice") == 0 ||
        e.CommandName.CompareTo("DecreasePrice") == 0)
    {
        // The Increase Price or Decrease Price Button has been clicked
        // Determine the ID of the product whose price was adjusted
        int productID =
            (int)SuppliersProducts.DataKeys[Convert.ToInt32(e.CommandArgument)].Value;
        // Determine how much to adjust the price
        decimal percentageAdjust;
        if (e.CommandName.CompareTo("IncreasePrice") == 0)
            percentageAdjust = 1.1M;
        else
            percentageAdjust = 0.9M;

        // Adjust the price
        ProductsBLL productInfo = new ProductsBLL();
        productInfo.UpdateProduct(percentageAdjust, productID);
    }
}

[Price +10%] (価格 +10%) または [Price -10%] (価格 -10%) ボタンがクリックされた行の ProductID を特定するには、GridView の DataKeys コレクションを参照する必要があります。 このコレクションでは、GridView の各行の DataKeyNames プロパティで指定されているフィールドの値が保持されます。 ObjectDataSource が GridView にバインドされたときに Visual Studio によって GridView のDataKeyNames プロパティが ProductID に設定されているため、DataKeys(rowIndex).Value では、指定された rowIndex の ProductID が提供されます。

ButtonField では、e.CommandArgument パラメーターによって、ボタンがクリックされた行の rowIndex が自動的に渡されます。 したがって、[Price +10%] (価格 +10%) または [Price -10%] (価格 -10%) ボタンがクリックされた行の ProductID を特定するには、Convert.ToInt32(SuppliersProducts.DataKeys(Convert.ToInt32(e.CommandArgument)).Value) を使います。

[Discontinue All Products] (すべての製品を廃止) ボタンと同様に、GridView のビュー状態を無効にしてある場合には GridView はポストバックのたびに基になるデータ ストアに再バインドされます。それにより、直ちに更新されて、ボタンのどれかをクリックして発生した価格変更が反映されます。 しかし、GridView でビュー状態を無効にしていない場合は、この変更を加えた後で、手動でデータを GridView に再バインドする必要があります。 これを実現するには、UpdateProduct メソッドを呼び出した直後に GridView の DataBind() メソッドを呼び出します。

図 20 に、Grandma Kelly's Homestead から提供されている製品を表示しているときのこのページを示します。 図 21 に、[Grandma's Boysenberry Spread] の [Price +10%] (価格 +10%) ボタンが 2 回クリックされ、[Northwoods Cranberry Sauce] の [Price -10%] (価格 -10%) ボタンが 1 回クリックされた後の結果を示します。

GridView に [価格 +10%] ボタンと [価格 -10%] ボタンが含まれている

図 20: GridView に [Price +10%] (価格 +10%) および [Price -10%] (価格 -10%) ボタンが含まれている (クリックするとフルサイズの画像が表示されます)

1 番目と 3 番目の製品の価格が [価格 +10%] ボタンと [価格 -10%] ボタンを使って更新された

図 21: [Price +10%] (価格 +10%) および [Price -10%] (価格 -10%) ボタンを使って 1 番目と 3 番目の製品の価格が更新された (クリックするとフルサイズの画像が表示されます)

Note

GridView (および DetailsView) では、その TemplateField に Button、LinkButton、または ImageButton を追加することもできます。 BoundField と同様に、これらのボタンをクリックするとポストバックが発生して、GridView の RowCommand イベントが発生します。 ただし、TemplateField にボタンを追加した場合、そのボタンの CommandArgument は、ButtonField を使っている場合のように自動的にその行のインデックスに設定されることはありません。 RowCommand イベント ハンドラー内で、クリックされたボタンの行インデックスを特定する必要がある場合は、次のようなコードを使って、手動で、TemplateField 内の宣言構文でそのボタンの CommandArgument プロパティを設定する必要があります:
<asp:Button runat="server" ... CommandArgument='<%# ((GridViewRow) Container).RowIndex %>'.

まとめ

GridView、DetailsView、FormView コントロールには、Button、LinkButton、または ImageButton を含めることができます。 このようなボタンをクリックすると、ポストバックが発生し、FormView および DetailsView コントロールでは ItemCommand イベントが発生し、GridView では RowCommand イベントが発生します。 これらのデータ Web コントロールには、レコードの削除や編集など、コマンド関連の一般的なアクションを処理する機能が組み込まれています。 しかし、クリックすると応答して独自のカスタム コードが実行されるボタンを使うこともできます。

これを実現するには、ItemCommand または RowCommand イベントのイベント ハンドラーを作成する必要があります。 このイベント ハンドラーでは、まず、受け取った CommandName 値を確認し、クリックされたボタンを特定してから、適切なカスタム アクションを実行します。 このチュートリアルでは、指定されたサプライヤーのすべての製品を廃止にするためや、特定の製品の価格を 10% 増減するためにボタンと ButtonField を使う方法について説明しました。

プログラミングに満足!

著者について

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