GridView のフッターから新しいレコードを挿入する (C#)
GridView コントロールでは、データの新しいレコードを挿入するための組み込みのサポートは提供されませんが、このチュートリアルでは、GridView を拡張して挿入インターフェイスを含める方法を示します。
はじめに
「データの挿入、更新、削除の概要」のチュートリアルで説明したように、GridView、DetailsView、および FormView の Web コントロールには、それぞれ組み込みのデータ変更機能が含まれています。 宣言型データ ソース コントロールと共に使用すると、コード行を 1 行も記述しなくても、これら 3 つの Web コントロールを、データが変更されるように迅速かつ簡単に構成できます。 組み込みの挿入、編集、削除の機能を提供するのは、残念ながら DetailsView と FormView のコントロールだけです。 GridView では、編集と削除のサポートのみが提供されます。 しかし、ほんの少し手間をかけるだけで、挿入インターフェイスを含むように GridView を拡張できます。
GridView に挿入機能を追加する場合、新しいレコードをどのように追加するかを決定し、挿入インターフェイスを作成し、新しいレコードを挿入するコードを記述する必要があります。 このチュートリアルでは、挿入インターフェイスを GridView のフッター行に追加する方法について説明します (図 1 を参照)。 各列のフッター セルに、適切なデータ コレクションのユーザー インターフェイス要素 (製品名の TextBox、サプライヤーの DropDownList など) が含まれています。 また、[追加] ボタンの列も必要です。クリックすることでポストバックが発生し、フッター行に指定された値を使用して新しいレコードが Products
テーブルに挿入されます。
図 1: フッター行に新しい製品を追加するためのインターフェイスが提供される (クリックするとフルサイズの画像が表示されます)
手順 1: GridView で製品情報を表示する
GridView のフッターに挿入インターフェイスを作成することを考える前に、データベース内の製品を一覧表示するページに GridView を追加することに注目しましょう。 まず、EnhancedGridView
フォルダー内のInsertThroughFooter.aspx
ページを開き、ツールボックスから GridView をデザイナーにドラッグして、GridView の ID
プロパティを Products
に設定します。 次に、GridView のスマート タグを使用して、ProductsDataSource
という名前の新しい ObjectDataSource にバインドします。
図 2: ProductsDataSource
という名前の新しい ObjectDataSource の作成 (クリックするとフルサイズの画像が表示されます)
ProductsBLL
クラスの GetProducts()
メソッドを使用して製品情報を取得するように ObjectDataSource を構成します。 このチュートリアルでは、挿入機能の追加にのみ注目し、編集や削除については考えないようにしましょう。 そのため、[INSERT] タブのドロップダウン リストが AddProduct()
に設定され、[UPDATE] タブと [DELETE] タブのドロップダウン リストが (なし) に設定されていることを確認します。
図 3: AddProduct
メソッドを ObjectDataSource の Insert()
メソッドにマップする (クリックするとフルサイズの画像が表示されます)
図 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>
図 5: すべての製品データのフィールドがページングされた GridView に表示される (クリックするとフルサイズの画像が表示されます)
手順 2: フッター行を追加する
GridView には、ヘッダーとデータの行に加えて、フッター行も含まれます。 ヘッダーとフッターの行は、GridView の ShowHeader
と ShowFooter
のプロパティの値に応じて表示されます。 フッター行は、ShowFooter
プロパティを true
に設定するだけで表示されます。 図 6 に示すように、ShowFooter
プロパティを true
に設定すると、フッター行がグリッドに追加されます。
図 6: フッター行を表示するには、ShowFooter
を True
に設定する (クリックするとフルサイズの画像が表示されます)
フッター行の背景色が濃い赤色になっていることに注目してください。 これは、「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 フィールドのフッターを変更します。
図 7: 空のフッター行がページング インターフェイス コントロールの上に表示される (クリックするとフルサイズの画像が表示されます)
手順 3: フッター行をカスタマイズする
「GridView コントロールで TemplateFields を使用する」のチュートリアルでは、(BoundFields や CheckBoxFields ではなく) TemplateFields を使用して特定の GridView の列の表示を大幅にカスタマイズする方法について説明しました。「データ変更インターフェイスをカスタマイズする」では、TemplateFields を使用して GridView の編集インターフェイスをカスタマイズする方法を確認しました。 TemplateField は、特定の種類の行に使用されるマークアップ、Web コントロール、およびデータバインド構文の組み合わせを定義する多数のテンプレートで構成されていることを思い出してください。 たとえば、ItemTemplate
は、読み取り専用行に使用されるテンプレートを指定し、EditItemTemplate
は、編集可能な行のテンプレートを定義します。
TemplateField には、ItemTemplate
や EditItemTemplate
と共に、フッター行の内容を指定する FooterTemplate
も含まれています。 したがって、各フィールドの挿入インターフェイスに必要な Web コントロールを FooterTemplate
に追加できます。 最初に、GridView 内のすべてのフィールドを TemplateFields に変換します。 これを行うには、GridView のスマート タグで [列の編集] リンクをクリックし、左下隅の各フィールドを選択し、[このフィールドを 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 は、ItemTemplate
と EditItemTemplate
に CheckBox Web コントロールを含む TemplateField に変換されています (ItemTemplate
のチェックボックスは無効になっています)。 読み取り専用の ProductID
BoundField は、ItemTemplate
と EditItemTemplate
の両方で 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
を選択し、ツールボックスからデザイナーに適切なコントロールをドラッグします。
図 9: 各フィールドの FooterTemplate
に適切な挿入インターフェイスを追加する (クリックするとフルサイズの画像が表示されます)
次の箇条書きは、GridView フィールドと追加する挿入インターフェイスを指定して列挙したものです。
ProductID
なし。ProductName
TextBox を追加し、そのID
をNewProductName
に設定します。 ユーザーが新しい製品名の値を入力するように、RequiredFieldValidator コントロールも追加します。SupplierID
なし。CategoryID
なし。QuantityPerUnit
TextBox を追加し、そのID
をNewQuantityPerUnit
に設定します。UnitPrice
NewUnitPrice
という名前の TextBox と CompareValidator を追加して、入力された値が 0 以上の通貨値になるようにします。UnitsInStock
ID
がNewUnitsInStock
に設定されている TextBox を使用します。 入力した値が 0 以上の整数値になるように、CompareValidator を含めます。UnitsOnOrder
ID
がNewUnitsOnOrder
に設定されている TextBox を使用します。 入力した値が 0 以上の整数値になるように、CompareValidator を含めます。ReorderLevel
ID
がNewReorderLevel
に設定されている TextBox を使用します。 入力した値が 0 以上の整数値になるように、CompareValidator を含めます。Discontinued
CheckBox を追加し、そのID
をNewDiscontinued
に設定します。CategoryName
DropDownList を追加し、そのID
をNewCategoryID
に設定します。 これをCategoriesDataSource
という名前の新しい ObjectDataSource にバインドし、CategoriesBLL
クラスのGetCategories()
メソッドを使用するように構成します。CategoryID
データ フィールドを値として使用して、DropDownList のListItem
にCategoryName
データ フィールドを表示させます。SupplierName
DropDownList を追加し、そのID
をNewSupplierID
に設定します。 これをSuppliersDataSource
という名前の新しい ObjectDataSource にバインドし、SuppliersBLL
クラスのGetSuppliers()
メソッドを使用するように構成します。SupplierID
データ フィールドを値として使用して、DropDownList のListItem
にCompanyName
データ フィールドを表示させます。
各検証コントロールの ForeColor
プロパティをクリアして、FooterStyle
CSS クラスの背景色に既定の赤ではなく白が使用されるようにします。 詳細な説明には ErrorMessage
プロパティも使用しますが、Text
プロパティはアスタリスクに設定します。 検証コントロールのテキストによって挿入インターフェイスが 2 行に折り返されないようにするには、検証コントロールを使用する FooterTemplate
ごとに FooterStyle
の Wrap
プロパティを false に設定します。 最後に、GridView の下に ValidationSummary コントロールを追加し、その ShowMessageBox
プロパティを true
に、その ShowSummary
を false
に設定します。
新しい製品を追加する場合は、CategoryID
と SupplierID
を指定する必要があります。 この情報は、CategoryName
と SupplierName
のフッター セルの DropDownLists を通じてキャプチャされます。 ここで CategoryID
と SupplierID
の TemplateFields ではなくこれらのフィールドを使用することを選択したのは、ユーザーがグリッドのデータ行で、ID の値ではなくカテゴリとサプライヤーの名前を表示したいことが多いためです。 CategoryID
と SupplierID
の値が CategoryName
と SupplierName
のフィールドの挿入インターフェイスでキャプチャされるようになったため、GridView から CategoryID
と SupplierID
の TemplateFields を削除できます。
同様に、ProductID
は新しい製品を追加するときに使用されないため、ProductID
TemplateField も削除できます。 ただし、グリッド内の ProductID
フィールドはそのままにしておきます。 挿入インターフェイスを構成する TextBoxes、DropDownLists、CheckBoxes、検証コントロールに加えて、クリックすることで新しい製品をデータベースに追加するロジックが実行される [追加] ボタンも必要です。 手順 4 で、ProductID
TemplateField の FooterTemplate
の挿入インターフェイスに [追加] ボタンを含めます。
さまざまな GridView フィールドの外観を自由に改善しましょう。 たとえば、UnitPrice
の値を通貨として書式設定し、UnitsInStock
、UnitsOnOrder
および ReorderLevel
のフィールドを右揃えし、TemplateFields の HeaderText
の値を更新することができます。
FooterTemplate
に多数の挿入インターフェイスを作成し、SupplierID
と CategoryID
の 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 で、フッターのデータを使用して新しいレコードを挿入する方法を示します。
図 10: GridView フッターで新しいレコードを追加するためのインターフェイスが提供される (クリックするとフルサイズの画像が表示されます)
手順 4: 挿入インターフェイスに [追加] ボタンを含める
フッター行の挿入インターフェイスには現在、ユーザーが新しい製品の情報入力を完了したことを示す手段がないため、挿入インターフェイスのどこかに [追加] ボタンを含める必要があります。 これは、既存の FooterTemplate
の列のいずれかに配置することも、この目的のためにグリッドに新しい列を追加することもできます。 このチュートリアルでは、ProductID
TemplateField の FooterTemplate
に [追加] ボタンを配置しましょう。
デザイナーから、GridView のスマート タグの [テンプレートの編集] リンクをクリックし、ドロップダウン リストから ProductID
フィールドの FooterTemplate
を選択します。 図 11 に示すように、Button Web コントロール (または必要に応じて LinkButton または ImageButton) をテンプレートに追加し、その ID を AddProduct
に、その CommandName
を [Insert] に、その Text
プロパティを [追加] に設定します。
図 11: ProductID
TemplateField の FooterTemplate
に [追加] ボタンを配置する (クリックするとフルサイズの画像が表示されます)
[追加] ボタンを含めたら、ブラウザーでページをテストします。 挿入インターフェイスで無効なデータを使用して [追加] ボタンをクリックすると、ポストバックがショートサーキットし、ValidationSummary コントロールが無効なデータを示します (図 12 を参照)。 適切なデータを入力すると、[追加] ボタンをクリックすることでポストバックが発生します。 ただし、データベースにはレコードが追加されません。 実際に挿入を実行するには、コードを少し記述する必要があります。
図 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 の全体的な幅は、収集する入力の数を少なくすることで縮小することができます。 新しい製品を追加するときに、UnitsOnOrder
、UnitsInStock
、および ReorderLevel
フィールドを収集する必要がない場合もあります。その場合、これらのフィールドは GridView から削除される可能性があります。
収集されたデータを調整するには、次の 2 つの方法のどちらかを使用します。
AddProduct
を引き続き使用します。UnitsOnOrder
、UnitsInStock
、およびReorderLevel
のフィールドに値が入ることが想定されます。Inserting
イベント ハンドラーで、挿入インターフェイスから削除されたこれらの入力に使用するハードコーディングされた既定値を指定します。UnitsOnOrder
、UnitsInStock
、および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行をドロップしてください。