GridView のフッターから新しいレコードを挿入する (C#)

作成者: Scott Mitchell

PDF のダウンロード

GridView コントロールでは、データの新しいレコードを挿入するための組み込みのサポートは提供されませんが、このチュートリアルでは、GridView を拡張して挿入インターフェイスを含める方法を示します。

はじめに

データの挿入、更新、削除の概要」のチュートリアルで説明したように、GridView、DetailsView、および FormView の Web コントロールには、それぞれ組み込みのデータ変更機能が含まれています。 宣言型データ ソース コントロールと共に使用すると、コード行を 1 行も記述しなくても、これら 3 つの Web コントロールを、データが変更されるように迅速かつ簡単に構成できます。 組み込みの挿入、編集、削除の機能を提供するのは、残念ながら DetailsView と FormView のコントロールだけです。 GridView では、編集と削除のサポートのみが提供されます。 しかし、ほんの少し手間をかけるだけで、挿入インターフェイスを含むように GridView を拡張できます。

GridView に挿入機能を追加する場合、新しいレコードをどのように追加するかを決定し、挿入インターフェイスを作成し、新しいレコードを挿入するコードを記述する必要があります。 このチュートリアルでは、挿入インターフェイスを GridView のフッター行に追加する方法について説明します (図 1 を参照)。 各列のフッター セルに、適切なデータ コレクションのユーザー インターフェイス要素 (製品名の TextBox、サプライヤーの DropDownList など) が含まれています。 また、[追加] ボタンの列も必要です。クリックすることでポストバックが発生し、フッター行に指定された値を使用して新しいレコードが Products テーブルに挿入されます。

The Footer Row Provides an Interface for Adding New Products

図 1: フッター行に新しい製品を追加するためのインターフェイスが提供される (クリックするとフルサイズの画像が表示されます)

手順 1: GridView で製品情報を表示する

GridView のフッターに挿入インターフェイスを作成することを考える前に、データベース内の製品を一覧表示するページに GridView を追加することに注目しましょう。 まず、EnhancedGridView フォルダー内のInsertThroughFooter.aspx ページを開き、ツールボックスから GridView をデザイナーにドラッグして、GridView の ID プロパティを Products に設定します。 次に、GridView のスマート タグを使用して、ProductsDataSource という名前の新しい ObjectDataSource にバインドします。

Create a New ObjectDataSource Named ProductsDataSource

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

ProductsBLL クラスの GetProducts() メソッドを使用して製品情報を取得するように ObjectDataSource を構成します。 このチュートリアルでは、挿入機能の追加にのみ注目し、編集や削除については考えないようにしましょう。 そのため、[INSERT] タブのドロップダウン リストが AddProduct() に設定され、[UPDATE] タブと [DELETE] タブのドロップダウン リストが (なし) に設定されていることを確認します。

Map the AddProduct Method to the ObjectDataSource s Insert() Method

図 3: AddProduct メソッドを ObjectDataSource の Insert() メソッドにマップする (クリックするとフルサイズの画像が表示されます)

Set the UPDATE and DELETE Tabs Drop-Down Lists to (None)

図 4: [UPDATE] と [DELETE] のタブのドロップダウン リストを (なし) に設定する (クリックするとフルサイズの画像が表示されます)

ObjectDataSource のデータ ソースの構成ウィザードが完了すると、Visual Studio によって、対応する各データ フィールドの GridView にフィールドが自動的に追加されます。 ここでは、Visual Studio によって追加されたすべてのフィールドをそのまま使用します。 このチュートリアルの後半で、新しいレコードを追加するときに値を指定する必要がないフィールドをいくつか削除します。

データベースには 80 個に近い製品があるため、ユーザーは新しいレコードを追加するために Web ページの下部までスクロールする必要があります。 そこで、ページングを有効にして、挿入インターフェイスを見やすく、アクセスしやすいものにしましょう。 GridView のスマート タグから [ページングを有効にする] チェック ボックスをオンにするだけで、ページングを有効にすることができます。

この時点で、GridView と ObjectDataSource の宣言型マークアップは次のようになります。

<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
    AllowPaging="True" EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="ProductID" HeaderText="ProductID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" HeaderText="ProductName" 
            SortExpression="ProductName" />
        <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" 
            SortExpression="SupplierID" />
        <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
            SortExpression="CategoryID" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit" 
            SortExpression="QuantityPerUnit" />
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" 
            SortExpression="UnitPrice" />
        <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock" 
            SortExpression="UnitsInStock" />
        <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder" 
            SortExpression="UnitsOnOrder" />
        <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel" 
            SortExpression="ReorderLevel" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" 
            SortExpression="Discontinued" />
        <asp:BoundField DataField="CategoryName" HeaderText="CategoryName" 
            ReadOnly="True" SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName" HeaderText="SupplierName" 
            ReadOnly="True" SortExpression="SupplierName" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    InsertMethod="AddProduct" OldValuesParameterFormatString="original_{0}" 
    SelectMethod="GetProducts" TypeName="ProductsBLL">
    <InsertParameters>
        <asp:Parameter Name="productName" Type="String" />
        <asp:Parameter Name="supplierID" Type="Int32" />
        <asp:Parameter Name="categoryID" Type="Int32" />
        <asp:Parameter Name="quantityPerUnit" Type="String" />
        <asp:Parameter Name="unitPrice" Type="Decimal" />
        <asp:Parameter Name="unitsInStock" Type="Int16" />
        <asp:Parameter Name="unitsOnOrder" Type="Int16" />
        <asp:Parameter Name="reorderLevel" Type="Int16" />
        <asp:Parameter Name="discontinued" Type="Boolean" />
    </InsertParameters>
</asp:ObjectDataSource>

All Product Data Fields are Displayed in a Paged GridView

図 5: すべての製品データのフィールドがページングされた GridView に表示される (クリックするとフルサイズの画像が表示されます)

GridView には、ヘッダーとデータの行に加えて、フッター行も含まれます。 ヘッダーとフッターの行は、GridView の ShowHeaderShowFooter のプロパティの値に応じて表示されます。 フッター行は、ShowFooter プロパティを true に設定するだけで表示されます。 図 6 に示すように、ShowFooter プロパティを true に設定すると、フッター行がグリッドに追加されます。

To Display the Footer Row, Set ShowFooter to True

図 6: フッター行を表示するには、ShowFooterTrue に設定する (クリックするとフルサイズの画像が表示されます)

フッター行の背景色が濃い赤色になっていることに注目してください。 これは、「ObjectDataSource でデータを表示する」のチュートリアルで DataWebControls のテーマを作成し、すべてのページに適用したことが原因です。 具体的には、GridView.skin ファイルによって FooterStyle CSS クラスが使用されるように FooterStyle プロパティが構成されています。 FooterStyle クラスは、次のように Styles.css で定義されます。

.FooterStyle
{
    background-color: #a33;
    color: White;
    text-align: right;
}

Note

前のチュートリアルで、GridView のフッター行の使用について説明しています。 必要に応じて、「GridView のフッターに概要情報を表示する」のチュートリアルに戻って、復習してください。

ShowFooter プロパティを true に設定してしばらくすると、ブラウザーに出力が表示されます。 現在、フッター行にはテキストまたは Web コントロールは含まれません。 手順 3 では、適切な挿入インターフェイスが含まれるように、各 GridView フィールドのフッターを変更します。

The Empty Footer Row is Displayed Above the Paging Interface Controls

図 7: 空のフッター行がページング インターフェイス コントロールの上に表示される (クリックするとフルサイズの画像が表示されます)

GridView コントロールで TemplateFields を使用する」のチュートリアルでは、(BoundFields や CheckBoxFields ではなく) TemplateFields を使用して特定の GridView の列の表示を大幅にカスタマイズする方法について説明しました。「データ変更インターフェイスをカスタマイズする」では、TemplateFields を使用して GridView の編集インターフェイスをカスタマイズする方法を確認しました。 TemplateField は、特定の種類の行に使用されるマークアップ、Web コントロール、およびデータバインド構文の組み合わせを定義する多数のテンプレートで構成されていることを思い出してください。 たとえば、ItemTemplate は、読み取り専用行に使用されるテンプレートを指定し、EditItemTemplate は、編集可能な行のテンプレートを定義します。

TemplateField には、ItemTemplateEditItemTemplate と共に、フッター行の内容を指定する FooterTemplate も含まれています。 したがって、各フィールドの挿入インターフェイスに必要な Web コントロールを FooterTemplate に追加できます。 最初に、GridView 内のすべてのフィールドを TemplateFields に変換します。 これを行うには、GridView のスマート タグで [列の編集] リンクをクリックし、左下隅の各フィールドを選択し、[このフィールドを TemplateField に変換する] リンクをクリックします。

Convert Each Field Into a TemplateField

図 8: 各フィールドを TemplateField に変換する

[このフィールドを TemplateField に変換する] をクリックすると、現在のフィールド型が同等の TemplateField に変換されます。 たとえば、BoundField は、対応するデータ フィールドを表示するラベルを含む ItemTemplate と、TextBox にデータ フィールドを表示する EditItemTemplate を持つ TemplateField によって置き換えられます。 ProductName BoundField は、次の TemplateField マークアップに変換されています。

<asp:TemplateField HeaderText="ProductName" SortExpression="ProductName">
    <EditItemTemplate>
        <asp:TextBox ID="TextBox1" runat="server" 
            Text='<%# Bind("ProductName") %>'></asp:TextBox>
    </EditItemTemplate>
    <ItemTemplate>
        <asp:Label ID="Label2" runat="server" 
            Text='<%# Bind("ProductName") %>'></asp:Label>
    </ItemTemplate>
</asp:TemplateField>

同様に、Discontinued CheckBoxField は、ItemTemplateEditItemTemplate に CheckBox Web コントロールを含む TemplateField に変換されています (ItemTemplate のチェックボックスは無効になっています)。 読み取り専用の ProductID BoundField は、ItemTemplateEditItemTemplate の両方で Label コントロールを含む TemplateField に変換されています。 つまり、既存の GridView フィールドを TemplateField に変換すると、既存のフィールドの機能を失うことなく、よりカスタマイズ可能な TemplateField にすばやく簡単に切り替えることができます。

作業中の GridView は編集をサポートしていないため、ItemTemplate だけを残し、EditItemTemplate は各 TemplateField から削除して構いません。 これを行った後、GridView の宣言型マークアップは次のようになります。

<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
    AllowPaging="True" EnableViewState="False" ShowFooter="True">
    <Columns>
        <asp:TemplateField HeaderText="ProductID" InsertVisible="False" 
            SortExpression="ProductID">
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server" 
                    Text='<%# Bind("ProductID") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="ProductName" SortExpression="ProductName">
            <ItemTemplate>
                <asp:Label ID="Label2" runat="server" 
                    Text='<%# Bind("ProductName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="SupplierID" SortExpression="SupplierID">
            <ItemTemplate>
                <asp:Label ID="Label3" runat="server" 
                    Text='<%# Bind("SupplierID") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="CategoryID" SortExpression="CategoryID">
            <ItemTemplate>
                <asp:Label ID="Label4" runat="server" 
                    Text='<%# Bind("CategoryID") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="QuantityPerUnit" 
            SortExpression="QuantityPerUnit">
            <ItemTemplate>
                <asp:Label ID="Label5" runat="server" 
                    Text='<%# Bind("QuantityPerUnit") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="UnitPrice" SortExpression="UnitPrice">
            <ItemTemplate>
                <asp:Label ID="Label6" runat="server" 
                    Text='<%# Bind("UnitPrice") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="UnitsInStock" 
            SortExpression="UnitsInStock">
            <ItemTemplate>
                <asp:Label ID="Label7" runat="server" 
                    Text='<%# Bind("UnitsInStock") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="UnitsOnOrder" 
            SortExpression="UnitsOnOrder">
            <ItemTemplate>
                <asp:Label ID="Label8" runat="server" 
                    Text='<%# Bind("UnitsOnOrder") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="ReorderLevel" 
            SortExpression="ReorderLevel">
            <ItemTemplate>
                <asp:Label ID="Label9" runat="server" 
                    Text='<%# Bind("ReorderLevel") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Discontinued" 
            SortExpression="Discontinued">
            <ItemTemplate>
                <asp:CheckBox ID="CheckBox1" runat="server" 
                    Checked='<%# Bind("Discontinued") %>' Enabled="false" />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="CategoryName" 
            SortExpression="CategoryName">
            <ItemTemplate>
                <asp:Label ID="Label10" runat="server" 
                    Text='<%# Bind("CategoryName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="SupplierName" 
            SortExpression="SupplierName">
            <ItemTemplate>
                <asp:Label ID="Label11" runat="server" 
                    Text='<%# Bind("SupplierName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

GridView フィールドがそれぞれ TemplateField に変換されたので、各フィールドの FooterTemplate に適切な挿入インターフェイスを入力できます。 一部のフィールドには挿入インターフェイスが入力されません (ProductID など)。その他は、新しい製品の情報を収集するために使用される Web コントロールによって異なります。

編集インターフェイスを作成するには、GridView のスマート タグから [テンプレートの編集] リンクを選択してください。 次に、ドロップダウン リストから適切なフィールドの FooterTemplate を選択し、ツールボックスからデザイナーに適切なコントロールをドラッグします。

Add the Appropriate Inserting Interface to Each Field s FooterTemplate

図 9: 各フィールドの FooterTemplate に適切な挿入インターフェイスを追加する (クリックするとフルサイズの画像が表示されます)

次の箇条書きは、GridView フィールドと追加する挿入インターフェイスを指定して列挙したものです。

  • ProductID なし。
  • ProductName TextBox を追加し、その IDNewProductName に設定します。 ユーザーが新しい製品名の値を入力するように、RequiredFieldValidator コントロールも追加します。
  • SupplierID なし。
  • CategoryID なし。
  • QuantityPerUnit TextBox を追加し、その IDNewQuantityPerUnit に設定します。
  • UnitPriceNewUnitPrice という名前の TextBox と CompareValidator を追加して、入力された値が 0 以上の通貨値になるようにします。
  • UnitsInStockIDNewUnitsInStock に設定されている TextBox を使用します。 入力した値が 0 以上の整数値になるように、CompareValidator を含めます。
  • UnitsOnOrderIDNewUnitsOnOrder に設定されている TextBox を使用します。 入力した値が 0 以上の整数値になるように、CompareValidator を含めます。
  • ReorderLevelIDNewReorderLevel に設定されている TextBox を使用します。 入力した値が 0 以上の整数値になるように、CompareValidator を含めます。
  • Discontinued CheckBox を追加し、その IDNewDiscontinued に設定します。
  • CategoryName DropDownList を追加し、その IDNewCategoryID に設定します。 これを CategoriesDataSource という名前の新しい ObjectDataSource にバインドし、CategoriesBLL クラスの GetCategories() メソッドを使用するように構成します。 CategoryID データ フィールドを値として使用して、DropDownList の ListItemCategoryName データ フィールドを表示させます。
  • SupplierName DropDownList を追加し、その IDNewSupplierID に設定します。 これを SuppliersDataSource という名前の新しい ObjectDataSource にバインドし、SuppliersBLL クラスの GetSuppliers() メソッドを使用するように構成します。 SupplierID データ フィールドを値として使用して、DropDownList の ListItemCompanyName データ フィールドを表示させます。

各検証コントロールの ForeColor プロパティをクリアして、FooterStyle CSS クラスの背景色に既定の赤ではなく白が使用されるようにします。 詳細な説明には ErrorMessage プロパティも使用しますが、Text プロパティはアスタリスクに設定します。 検証コントロールのテキストによって挿入インターフェイスが 2 行に折り返されないようにするには、検証コントロールを使用する FooterTemplate ごとに FooterStyleWrap プロパティを false に設定します。 最後に、GridView の下に ValidationSummary コントロールを追加し、その ShowMessageBox プロパティを true に、その ShowSummaryfalse に設定します。

新しい製品を追加する場合は、CategoryIDSupplierID を指定する必要があります。 この情報は、CategoryNameSupplierName のフッター セルの DropDownLists を通じてキャプチャされます。 ここで CategoryIDSupplierID の TemplateFields ではなくこれらのフィールドを使用することを選択したのは、ユーザーがグリッドのデータ行で、ID の値ではなくカテゴリとサプライヤーの名前を表示したいことが多いためです。 CategoryIDSupplierID の値が CategoryNameSupplierName のフィールドの挿入インターフェイスでキャプチャされるようになったため、GridView から CategoryIDSupplierID の TemplateFields を削除できます。

同様に、ProductID は新しい製品を追加するときに使用されないため、ProductID TemplateField も削除できます。 ただし、グリッド内の ProductID フィールドはそのままにしておきます。 挿入インターフェイスを構成する TextBoxes、DropDownLists、CheckBoxes、検証コントロールに加えて、クリックすることで新しい製品をデータベースに追加するロジックが実行される [追加] ボタンも必要です。 手順 4 で、ProductID TemplateField の FooterTemplate の挿入インターフェイスに [追加] ボタンを含めます。

さまざまな GridView フィールドの外観を自由に改善しましょう。 たとえば、UnitPrice の値を通貨として書式設定し、UnitsInStockUnitsOnOrder および ReorderLevel のフィールドを右揃えし、TemplateFields の HeaderText の値を更新することができます。

FooterTemplate に多数の挿入インターフェイスを作成し、SupplierIDCategoryID の TemplateFields を削除し、TemplateFields の書式設定と配置を通じてグリッドの外観を改善すると、GridView の宣言型マークアップは次のようになります。

<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
    AllowPaging="True" EnableViewState="False" ShowFooter="True">
    <Columns>
        <asp:TemplateField HeaderText="ProductID" InsertVisible="False" 
            SortExpression="ProductID">
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server" 
                    Text='<%# Bind("ProductID") %>'></asp:Label>
            </ItemTemplate>
            <ItemStyle HorizontalAlign="Center" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
            <ItemTemplate>
                <asp:Label ID="Label2" runat="server" 
                    Text='<%# Bind("ProductName") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewProductName" runat="server"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
                    runat="server" ControlToValidate="NewProductName"
                    Display="Dynamic"  ForeColor="
                    ErrorMessage="You must enter a name for the new product.">
                    * </asp:RequiredFieldValidator>
            </FooterTemplate>
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Category" SortExpression="CategoryName">
            <ItemTemplate>
                <asp:Label ID="Label10" runat="server" 
                    Text='<%# Bind("CategoryName") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:DropDownList ID="NewCategoryID" runat="server" 
                    DataSourceID="CategoriesDataSource"
                    DataTextField="CategoryName" DataValueField="CategoryID">
                </asp:DropDownList>
                <asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
                    OldValuesParameterFormatString="original_{0}" 
                    SelectMethod="GetCategories" TypeName="CategoriesBLL">
                </asp:ObjectDataSource>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Supplier" SortExpression="SupplierName">
            <ItemTemplate>
                <asp:Label ID="Label11" runat="server" 
                    Text='<%# Bind("SupplierName") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:DropDownList ID="NewSupplierID" runat="server" 
                    DataSourceID="SuppliersDataSource"
                    DataTextField="CompanyName" DataValueField="SupplierID">
                </asp:DropDownList><asp:ObjectDataSource ID="SuppliersDataSource" 
                    runat="server" OldValuesParameterFormatString="original_{0}" 
                    SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
                </asp:ObjectDataSource>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Qty/Unit" SortExpression="QuantityPerUnit">
            <ItemTemplate>
                <asp:Label ID="Label5" runat="server" 
                    Text='<%# Bind("QuantityPerUnit") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewQuantityPerUnit" runat="server"></asp:TextBox>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
            <ItemTemplate>
                <asp:Label ID="Label6" runat="server" 
                    Text='<%# Bind("UnitPrice", "{0:c}") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                $<asp:TextBox ID="NewUnitPrice" runat="server" Columns="8" />
                <asp:CompareValidator ID="CompareValidator1" runat="server" 
                    ControlToValidate="NewUnitPrice"
                    ErrorMessage="You must enter a valid currency value greater than 
                        or equal to 0.00. Do not include the currency symbol."
                    ForeColor="" Operator="GreaterThanEqual" Type="Currency" 
                    ValueToCompare="0" Display="Dynamic">
                    * </asp:CompareValidator>
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Units In Stock" 
            SortExpression="Units In Stock">
            <ItemTemplate>
                <asp:Label ID="Label7" runat="server" 
                    Text='<%# Bind("UnitsInStock") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewUnitsInStock" runat="server" Columns="5" />
                <asp:CompareValidator ID="CompareValidator2" runat="server" 
                    ControlToValidate="NewUnitsInStock" Display="Dynamic" 
                    ErrorMessage="You must enter a valid numeric value for units 
                        in stock that's greater than or equal to zero."
                    ForeColor="" Operator="GreaterThanEqual" Type="Integer" 
                        ValueToCompare="0">*</asp:CompareValidator>
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Units On Order" SortExpression="UnitsOnOrder">
            <ItemTemplate>
                <asp:Label ID="Label8" runat="server" 
                    Text='<%# Bind("UnitsOnOrder") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewUnitsOnOrder" runat="server" Columns="5" />
                <asp:CompareValidator ID="CompareValidator3" runat="server" 
                    ControlToValidate="NewUnitsOnOrder" Display="Dynamic" 
                    ErrorMessage="You must enter a valid numeric value for units on 
                        order that's greater than or equal to zero."
                    ForeColor="" Operator="GreaterThanEqual" Type="Integer" 
                    ValueToCompare="0">*</asp:CompareValidator>
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Reorder Level" SortExpression="ReorderLevel">
            <ItemTemplate>
                <asp:Label ID="Label9" runat="server" 
                    Text='<%# Bind("ReorderLevel") %>'></asp:Label>
            </ItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="NewReorderLevel" runat="server" Columns="5" />
                <asp:CompareValidator ID="CompareValidator4" runat="server" 
                    ControlToValidate="NewReorderLevel" Display="Dynamic" 
                    ErrorMessage="You must enter a valid numeric value for reorder 
                        level that's greater than or equal to zero."
                    ForeColor="" Operator="GreaterThanEqual" Type="Integer" 
                    ValueToCompare="0">*</asp:CompareValidator>
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <FooterStyle Wrap="False" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
            <ItemTemplate>
                <asp:CheckBox ID="CheckBox1" runat="server" 
                    Checked='<%# Bind("Discontinued") %>' Enabled="false" />
            </ItemTemplate>
            <FooterTemplate>
                <asp:CheckBox ID="NewDiscontinued" runat="server" />
            </FooterTemplate>
            <ItemStyle HorizontalAlign="Center" />
            <FooterStyle HorizontalAlign="Center" />
        </asp:TemplateField>
    </Columns>
</asp:GridView>

ブラウザーで表示すると、GridView のフッター行に、完成した挿入インターフェイスが含まれるようになりました (図 10 を参照)。 この時点では、ユーザーが新しい製品のデータを入力し、データベースに新しいレコードを挿入したいことを示す手段が挿入インターフェイスに含まれていません。 フッターに入力されたデータが Products データベース内の新しいレコードにどのように変換されるかについても、まだ説明していません。 手順 4 で、挿入インターフェイスに [追加] ボタンを含める方法と、これがクリックされたときにポストバックでコードがどのように実行されるかについて説明します。 手順 5 で、フッターのデータを使用して新しいレコードを挿入する方法を示します。

The GridView Footer Provides an Interface for Adding a New Record

図 10: GridView フッターで新しいレコードを追加するためのインターフェイスが提供される (クリックするとフルサイズの画像が表示されます)

手順 4: 挿入インターフェイスに [追加] ボタンを含める

フッター行の挿入インターフェイスには現在、ユーザーが新しい製品の情報入力を完了したことを示す手段がないため、挿入インターフェイスのどこかに [追加] ボタンを含める必要があります。 これは、既存の FooterTemplate の列のいずれかに配置することも、この目的のためにグリッドに新しい列を追加することもできます。 このチュートリアルでは、ProductID TemplateField の FooterTemplate に [追加] ボタンを配置しましょう。

デザイナーから、GridView のスマート タグの [テンプレートの編集] リンクをクリックし、ドロップダウン リストから ProductID フィールドの FooterTemplate を選択します。 図 11 に示すように、Button Web コントロール (または必要に応じて LinkButton または ImageButton) をテンプレートに追加し、その ID を AddProduct に、その CommandName を [Insert] に、その Text プロパティを [追加] に設定します。

Place the Add Button in the ProductID TemplateField s FooterTemplate

図 11: ProductID TemplateField の FooterTemplate に [追加] ボタンを配置する (クリックするとフルサイズの画像が表示されます)

[追加] ボタンを含めたら、ブラウザーでページをテストします。 挿入インターフェイスで無効なデータを使用して [追加] ボタンをクリックすると、ポストバックがショートサーキットし、ValidationSummary コントロールが無効なデータを示します (図 12 を参照)。 適切なデータを入力すると、[追加] ボタンをクリックすることでポストバックが発生します。 ただし、データベースにはレコードが追加されません。 実際に挿入を実行するには、コードを少し記述する必要があります。

The Add Button s Postback is Short Circuited if There is Invalid Data in the Inserting Interface

図 12: 挿入インターフェイスに無効なデータがある場合、[追加] ボタンのポストバックがショートサーキットされる (クリックするとフルサイズの画像が表示されます)

Note

挿入インターフェイスの検証コントロールが検証グループに割り当てられていません。 これは、挿入インターフェイスのみがページ上の検証コントロールとして存在する限り、正常に動作します。 ただし、ページに他の検証コントロール (グリッドの編集インターフェイスの検証コントロールなど) がある場合は、挿入インターフェイスの検証コントロールと [追加] ボタンの ValidationGroup プロパティに同じ値を割り当てて、これらのコントロールを特定の検証グループに関連付ける必要があります。 ページ上で検証コントロールとボタンを検証グループに分割する方法の詳細については、ASP.NET 2.0 での検証コントロールの分析に関する記事を参照してください。

手順 5: Products テーブルに新しいレコードを挿入する

GridView の組み込みの編集機能を利用すると、更新の実行に必要なすべての作業を GridView が自動的に処理します。 特に、[更新] ボタンをクリックすると、編集インターフェイスから入力された値が ObjectDataSource の UpdateParameters コレクション内のパラメーターにコピーされ、ObjectDataSource のUpdate() メソッドを呼び出して更新が開始されます。 GridView にはこのような挿入用の組み込み機能がないため、ObjectDataSource の Insert() メソッドを呼び出し、挿入インターフェイスから ObjectDataSource の InsertParameters コレクションに値をコピーするコードを実装する必要があります。

この挿入ロジックは、[追加] ボタンがクリックされた後に実行する必要があります。 「GridView にボタンを追加し、応答する」のチュートリアルで説明したように、GridView の Button、LinkButton、または ImageButton がクリックされると、常にポストバック時に GridView の RowCommand イベントが発生します。 このイベントは、フッター行の [追加] ボタンのように、Button、LinkButton、ImageButton が明示的に追加されたか、GridView によって自動的に追加された ([並べ替えを有効にする] が選択されている場合に各列の上部にある LinkButtons や、[ページングを有効にする] が選択されている場合にページング インターフェイスにある LinkButtons など) 場合に発生します。

そのため、[追加] ボタンをクリックしたユーザーに応答するには、GridView の RowCommand イベントにイベント ハンドラーを作成する必要があります。 このイベントは、GridView の Button、LinkButton、または ImageButton の "いずれか" がクリックされるたびに発生するため、イベント ハンドラーに渡された CommandName プロパティが [追加] ボタン (Insert) の CommandName 値にマップされている場合にのみ、挿入ロジックを続行することが重要です。 さらに、検証コントロールが有効なデータを報告する場合にのみ、続行する必要があります。 これに対応するには、次のコードを使用して RowCommand イベントのイベント ハンドラーを作成します。

protected void Products_RowCommand(object sender, GridViewCommandEventArgs e)
{
    // Insert data if the CommandName == "Insert" 
    // and the validation controls indicate valid data...
    if (e.CommandName == "Insert" && Page.IsValid)
    {
        // TODO: Insert new record...
    }
}

Note

なぜイベント ハンドラーが Page.IsValid プロパティのチェックを行うのか疑問に思うかもしれません。 結局のところ、挿入インターフェイスで無効なデータが提供されている場合、ポストバックは抑制されないのでしょうか? ユーザーが JavaScript を無効にしていないか、クライアント側の検証ロジックを回避する手順を実行している限り、この前提は正しいです。 つまり、クライアント側の検証だけに依存してはいけないということです。データを操作する前に、サーバー側の有効性のチェックを常に実行する必要があります。

手順 1 では、その Insert() メソッドが ProductsBLL クラスの AddProduct メソッドにマップされるように ProductsDataSource ObjectDataSource を作成しました。 ObjectDataSource の Insert() メソッドを呼び出すだけで、Products テーブルに新しいレコードを挿入することができます。

protected void Products_RowCommand(object sender, GridViewCommandEventArgs e)
{
    // Insert data if the CommandName == "Insert" 
    // and the validation controls indicate valid data...
    if (e.CommandName == "Insert" && Page.IsValid)
    {
        // Insert new record
        ProductsDataSource.Insert();
    }
}

Insert() メソッドが呼び出されたので、後は挿入インターフェイスから ProductsBLL クラスの AddProduct メソッドに渡されたパラメーターに値をコピーするだけです。 「挿入、更新、削除に関連付けられているイベントを調べる」のチュートリアルで説明したように、これは ObjectDataSource の Inserting イベントを使用して実現できます。 Inserting イベントで、プログラムで Products GridView のフッター行からコントロールを参照し、その値を e.InputParameters コレクションに割り当てる必要があります。 ユーザーが ReorderLevel TextBox を空白のままにするなどして値を省略した場合は、データベースに挿入される値を NULL と指定する必要があります。 AddProducts メソッドは null 許容型データベース フィールドに対して null 許容型を受け入れるので、ユーザー入力が省略された場合は単に null 許容型を使用し、その値を null に設定します。

protected void ProductsDataSource_Inserting
    (object sender, ObjectDataSourceMethodEventArgs e)
{
    // Programmatically reference Web controls in the inserting interface...
    TextBox NewProductName = 
        (TextBox)Products.FooterRow.FindControl("NewProductName");
    DropDownList NewCategoryID = 
        (DropDownList)Products.FooterRow.FindControl("NewCategoryID");
    DropDownList NewSupplierID = 
        (DropDownList)Products.FooterRow.FindControl("NewSupplierID");
    TextBox NewQuantityPerUnit = 
        (TextBox)Products.FooterRow.FindControl("NewQuantityPerUnit");
    TextBox NewUnitPrice = 
        (TextBox)Products.FooterRow.FindControl("NewUnitPrice");
    TextBox NewUnitsInStock = 
        (TextBox)Products.FooterRow.FindControl("NewUnitsInStock");
    TextBox NewUnitsOnOrder = 
        (TextBox)Products.FooterRow.FindControl("NewUnitsOnOrder");
    TextBox NewReorderLevel = 
        (TextBox)Products.FooterRow.FindControl("NewReorderLevel");
    CheckBox NewDiscontinued = 
        (CheckBox)Products.FooterRow.FindControl("NewDiscontinued");
    // Set the ObjectDataSource's InsertParameters values...
    e.InputParameters["productName"] = NewProductName.Text;
    
    e.InputParameters["supplierID"] = 
        Convert.ToInt32(NewSupplierID.SelectedValue);
    e.InputParameters["categoryID"] = 
        Convert.ToInt32(NewCategoryID.SelectedValue);
    
    string quantityPerUnit = null;
    if (!string.IsNullOrEmpty(NewQuantityPerUnit.Text))
        quantityPerUnit = NewQuantityPerUnit.Text;
    e.InputParameters["quantityPerUnit"] = quantityPerUnit;
    decimal? unitPrice = null;
    if (!string.IsNullOrEmpty(NewUnitPrice.Text))
        unitPrice = Convert.ToDecimal(NewUnitPrice.Text);
    e.InputParameters["unitPrice"] = unitPrice;
    short? unitsInStock = null;
    if (!string.IsNullOrEmpty(NewUnitsInStock.Text))
        unitsInStock = Convert.ToInt16(NewUnitsInStock.Text);
    e.InputParameters["unitsInStock"] = unitsInStock;
    short? unitsOnOrder = null;
    if (!string.IsNullOrEmpty(NewUnitsOnOrder.Text))
        unitsOnOrder = Convert.ToInt16(NewUnitsOnOrder.Text);
    e.InputParameters["unitsOnOrder"] = unitsOnOrder;
    short? reorderLevel = null;
    if (!string.IsNullOrEmpty(NewReorderLevel.Text))
        reorderLevel = Convert.ToInt16(NewReorderLevel.Text);
    e.InputParameters["reorderLevel"] = reorderLevel;
    
    e.InputParameters["discontinued"] = NewDiscontinued.Checked;
}

Inserting イベント ハンドラーが完了したら、GridView のフッター行を使用して Products データベース テーブルに新しいレコードを追加できます。 この後、新しい製品をいくつか追加してみてください。

追加操作の拡張とカスタマイズ

現時点では、[追加] ボタンをクリックすると、データベース テーブルに新しいレコードが追加されますが、レコードが正常に追加されたことを示す視覚的なフィードバックは提供されません。 Label Web コントロールまたはクライアント側のアラート ボックスが、挿入が正常に完了したことをユーザーに通知するのが理想的です。 これは、読者のための演習として残しておきます。

このチュートリアルで使用する GridView は、リストされている製品に並べ替え順序を適用することも、エンド ユーザーにデータの並べ替えを許可することもありません。 結果的に、レコードはデータベース内で主キー フィールドによって並べ替えられます。 新しいレコードにはそれぞれ、最後のレコードより大きい ProductID 値が含まれるため、追加された新しい製品は、グリッドの末尾に付け加えられます。 そのため、場合によっては、新しいレコードを追加した後、GridView の最終ページにユーザーを自動的に送る必要があります。 これを実現するには、RowCommand イベント ハンドラーでの ProductsDataSource.Insert() の呼び出しの後に次のコード行を追加して、データを GridView にバインドした後、最終ページにユーザーを送る必要があることを示します。

// Indicate that the user needs to be sent to the last page
SendUserToLastPage = true;

SendUserToLastPage は、最初に false の値が割り当てられたページ レベルのブール値変数です。 GridView の DataBound イベント ハンドラーで、SendUserToLastPage が false の場合、ユーザーを最終ページに送るように PageIndex プロパティが更新されます。

protected void Products_DataBound(object sender, EventArgs e)
{
    // Send user to last page of data, if needed
    if (SendUserToLastPage)
        Products.PageIndex = Products.PageCount - 1;
}

PageIndex プロパティを (RowCommandイベント ハンドラーではなく ) DataBound イベント ハンドラーで設定するのは、RowCommand イベント ハンドラーが起動したときに、新しいレコードをまだ Products データベース テーブルに追加していないためです。 そのため、RowCommand イベント ハンドラーでは、最終ページ インデックス (PageCount - 1) は、新しい製品が追加される "前" の最終ページ インデックスを表します。 追加される製品の大半で、新しい製品を追加した後の最終ページ インデックスは同じです。 ただし、追加された製品が新しい最終ページ インデックスになると、RowCommand イベント ハンドラーで PageIndex を誤って更新した場合に、新しい最終ページ インデックスではなく、その前の最終ページ (新しい製品を追加する前の最終ページ インデックス) に移動します。 DataBound イベント ハンドラーは、新しい製品が追加され、データがグリッドに再度バインドされた後に発生するため、PageIndex プロパティを設定することで、正しい最終ページ インデックスが取得されていることがわかります。

最終的に、このチュートリアルで使用される GridView の幅は、非常に広くなります。これは、新しい製品を追加するために収集する必要があるフィールドの数が原因です。 この幅の広さのため、DetailsView の垂直レイアウトが推奨される場合があります。 GridView の全体的な幅は、収集する入力の数を少なくすることで縮小することができます。 新しい製品を追加するときに、UnitsOnOrderUnitsInStock、および ReorderLevel フィールドを収集する必要がない場合もあります。その場合、これらのフィールドは GridView から削除される可能性があります。

収集されたデータを調整するには、次の 2 つの方法のどちらかを使用します。

  • AddProduct を引き続き使用します。UnitsOnOrderUnitsInStock、および ReorderLevel のフィールドに値が入ることが想定されます。 Inserting イベント ハンドラーで、挿入インターフェイスから削除されたこれらの入力に使用するハードコーディングされた既定値を指定します。
  • UnitsOnOrderUnitsInStock、および ReorderLevel のフィールドの入力を受け入れない ProductsBLL クラスに AddProduct メソッドの新しいオーバーロードを作成します。 その後、ASP.NET ページで、この新しいオーバーロードを使用するように ObjectDataSource を構成します。

どちらのオプションも同様に機能します。 過去のチュートリアルでは、後者のオプションを使用して、ProductsBLL クラスの UpdateProduct メソッドに複数のオーバーロードを作成しました。

まとめ

GridView には DetailsView と FormView にあるような組み込みの挿入機能はありませんが、少しの労力で、挿入インターフェイスをフッター行に追加できます。 その ShowFooter プロパティを true に設定するだけで、GridView でフッター行を表示できます。 フッター行の内容は、フィールドを TemplateField に変換し、挿入インターフェイスを FooterTemplate に追加することで、フィールドごとにカスタマイズすることができます。 このチュートリアルで説明したように、FooterTemplate には Buttons、TextBoxes、DropDownLists、CheckBoxes、データ ドリブン Web コントロール (DropDownLists など) に入力するためのデータ ソース コントロール、および検証コントロールを含めることができます。 ユーザーの入力を収集するためのコントロールと共に、[追加] ボタン、LinkButton、または ImageButton が必要です。

[追加] ボタンをクリックすると、ObjectDataSource の Insert() メソッドが呼び出され、挿入ワークフローが開始されます。 その後、ObjectDataSource は構成された insert メソッド (このチュートリアルでは ProductsBLL クラスの AddProduct メソッド) を呼び出します。 insert メソッドが呼び出される前に、GridView の挿入インターフェイスの値を ObjectDataSource の InsertParameters コレクションにコピーする必要があります。 これは、ObjectDataSource の Inserting イベント ハンドラーで挿入インターフェイス Web コントロールをプログラムで参照することで実現できます。

GridView の外観を改善させる手法についてのチュートリアルが完了しました。 次の一連のチュートリアルでは、画像、PDF、Word 文書などのバイナリ データやデータ Web コントロールを操作する方法について説明します。

プログラミングに満足!

著者について

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

特別な感謝

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