このチュートリアルでは、DropDownList と CheckBox を含む、DataList のより豊富な編集インターフェイスを作成します。
はじめに
DataList の EditItemTemplate 内のマークアップと Web コントロールは、その編集可能なインターフェイスを定義します。 これまでに説明したすべての編集可能な DataList の例では、編集可能なインターフェイスは TextBox Web コントロールで構成されています。
前のチュートリアルでは、検証コントロールを追加することで編集時のユーザー エクスペリエンスを改善しました。
EditItemTemplate をさらに拡張して、DropDownList、RadioButtonList、Calendar など、TextBox 以外の Web コントロールを含めることもできます。 TextBox と同様に、編集インターフェイスをカスタマイズして他の Web コントロールを含める場合は、以下の手順を使います。
- Web コントロールを
EditItemTemplateに追加します。 - databinding 構文を使って、対応するデータ フィールド値を適切なプロパティに割り当てます。
-
UpdateCommandイベント ハンドラーで、プログラムによって Web コントロール値にアクセスし、それを適切な BLL メソッドに渡します。
このチュートリアルでは、DropDownList と CheckBox を含む、DataList のより豊富な編集インターフェイスを作成します。 特に、製品情報を一覧表示し、製品名、仕入先、カテゴリ、生産中止状態を更新できるようにする DataList を作成します (図 1 を参照)。
図 1: 編集インターフェイスには、TextBox、2 つの DropDownList、CheckBox が含まれています (クリックするとフルサイズの画像が表示されます)
手順 1: 製品情報の表示
DataList の編集可能なインターフェイスを作成する前に、まず読み取り専用インターフェイスを構築する必要があります。 まず、CustomizedUI.aspx フォルダーから EditDeleteDataList ページを開き、デザイナーから DataList をページに追加して、その ID プロパティを Products に設定します。 DataList のスマート タグから、新しい ObjectDataSource を作成します。 この新しい ObjectDataSource ProductsDataSource に名前を付け、ProductsBLL クラスの GetProducts メソッドからデータを取得するように構成します。 以前の編集可能な DataList チュートリアルと同様に、ビジネス ロジック レイヤーに直接移動して、編集した製品の情報を更新します。 そのため、[UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを (None) に設定します。
図 2: [UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを (None) に設定します (クリックするとフルサイズの画像が表示されます)
ObjectDataSource を構成すると、Visual Studio によって、返された各データ フィールドの名前と値を一覧表示する DataList の既定の ItemTemplate が作成されます。 テンプレートの ItemTemplate 要素に製品名がカテゴリ名、仕入先名、価格、生産中止状態と共に一覧表示されるように、<h4> を変更します。 さらに、[Edit] ボタンを追加し、その CommandName プロパティが Edit に設定されていることを確認します。
ItemTemplate の宣言型マークアップは次のとおりです。
<ItemTemplate>
<h4>
<asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>' />
</h4>
<table border="0">
<tr>
<td class="ProductPropertyLabel">Category:</td>
<td class="ProductPropertyValue">
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Eval("CategoryName") %>' />
</td>
<td class="ProductPropertyLabel">Supplier:</td>
<td class="ProductPropertyValue">
<asp:Label ID="SupplierNameLabel" runat="server"
Text='<%# Eval("SupplierName") %>' />
</td>
</tr>
<tr>
<td class="ProductPropertyLabel">Discontinued:</td>
<td class="ProductPropertyValue">
<asp:Label ID="DiscontinuedLabel" runat="server"
Text='<%# Eval("Discontinued") %>' />
</td>
<td class="ProductPropertyLabel">Price:</td>
<td class="ProductPropertyValue">
<asp:Label ID="UnitPriceLabel" runat="server"
Text='<%# Eval("UnitPrice", "{0:C}") %>' />
</td>
</tr>
<tr>
<td colspan="4">
<asp:Button runat="Server" ID="EditButton"
Text="Edit" CommandName="Edit" />
</td>
</tr>
</table>
<br />
</ItemTemplate>
上記のマークアップは、製品名に <h4> 見出しを使い、残りのフィールドに 4 列の <table> を使って製品情報をレイアウトします。
ProductPropertyLabel で定義されている ProductPropertyValue および Styles.css CSS クラスについては、前のチュートリアルで説明しました。 図 3 は、ブラウザーで見たときのこれまでの進捗を示しています。
図 3: 各製品の名前、仕入先、カテゴリ、生産中止状態、価格が表示されます (クリックするとフルサイズの画像が表示されます)
手順 2: Web コントロールの編集インターフェイスへの追加
カスタマイズされた DataList 編集インターフェイスを構築する最初の手順は、必要な Web コントロールを EditItemTemplate に追加することです。 特に、DropDownList がカテゴリ用と、もう 1 つを仕入先用に、CheckBox が生産中止状態用に必要です。 この例では製品の価格は編集できないため、引き続き Label Web コントロールを使って価格を表示できます。
編集インターフェイスをカスタマイズするには、DataList のスマート タグから [テンプレートの編集] リンクをクリックし、ドロップダウン リストから EditItemTemplate オプションを選びます。 DropDownList を EditItemTemplate に追加し、その ID を Categories に設定します。
図 4: カテゴリの DropDownList を追加します (クリックするとフルサイズの画像が表示されます)
次に、DropDownList のスマート タグから、[データ ソースの選択] オプションを選び、CategoriesDataSource という名前の新しい ObjectDataSource を作成します。 この ObjectDataSource を、CategoriesBLL クラスの GetCategories() メソッドを使うように構成します (図 5 を参照)。 次に、DropDownList のデータ ソース構成ウィザードで、各 ListItem の Text と Value プロパティに使うデータ フィールドの入力を求められます。 図 6 に示すように、DropDownList に CategoryName データ フィールドを表示させ、値として CategoryID を使います。
図 5: CategoriesDataSource という名前の新しい ObjectDataSource を作成します (クリックするとフルサイズの画像が表示されます)
図 6: DropDownList の表示フィールドと値フィールドを構成します (クリックするとフルサイズの画像が表示されます)
この一連の手順を繰り返して、仕入先の DropDownList を作成します。 この DropDownList の ID を Suppliers に設定し、その ObjectDataSource に SuppliersDataSource という名前を付けます。
2 つの DropDownList を追加した後、生産中止状態を示す CheckBox と製品名を示す TextBox を追加します。 CheckBox と TextBox の ID をそれぞれ Discontinued と ProductName に設定します。 RequiredFieldValidator を追加して、ユーザーが製品名の値を確実に指定できるようにします。
最後に、[Update] ボタンと [Cancel] ボタンを追加します。 これら 2 つのボタンでは、CommandName プロパティをそれぞれ Update と Cancel に設定することが必要であることに注意してください。
編集インターフェイスを好きなようにレイアウトします。 次の宣言構文とスクリーン ショットが示すように、読み取り専用インターフェイスから同じ 4 列の <table> レイアウトを使うことにしました。
<EditItemTemplate>
<h4>
<asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>' />
</h4>
<table border="0">
<tr>
<td class="ProductPropertyLabel">Name:</td>
<td colspan="3" class="ProductPropertyValue">
<asp:TextBox runat="server" ID="ProductName" Width="90%" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName"
ErrorMessage="You must enter a name for the product."
runat="server">*</asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td class="ProductPropertyLabel">Category:</td>
<td class="ProductPropertyValue">
<asp:DropDownList ID="Categories" runat="server"
DataSourceID="CategoriesDataSource"
DataTextField="CategoryName" DataValueField="CategoryID" />
</td>
<td class="ProductPropertyLabel">Supplier:</td>
<td class="ProductPropertyValue">
<asp:DropDownList ID="Suppliers" DataTextField="CompanyName"
DataSourceID="SuppliersDataSource"
DataValueField="SupplierID" runat="server" />
</td>
</tr>
<tr>
<td class="ProductPropertyLabel">Discontinued:</td>
<td class="ProductPropertyValue">
<asp:CheckBox runat="server" id="Discontinued" />
</td>
<td class="ProductPropertyLabel">Price:</td>
<td class="ProductPropertyValue">
<asp:Label ID="UnitPriceLabel" runat="server"
Text='<%# Eval("UnitPrice", "{0:C}") %>' />
</td>
</tr>
<tr>
<td colspan="4">
<asp:Button runat="Server" ID="UpdateButton" CommandName="Update"
Text="Update" />
<asp:Button runat="Server" ID="CancelButton" CommandName="Cancel"
Text="Cancel" CausesValidation="False" />
</td>
</tr>
</table>
<br />
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL">
</asp:ObjectDataSource>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetSuppliers"
TypeName="SuppliersBLL">
</asp:ObjectDataSource>
</EditItemTemplate>
図 7: 編集インターフェイスが読み取り専用インターフェイスと同じようにレイアウトされています (クリックするとフルサイズの画像が表示されます)
手順 3: EditCommand と CancelCommand イベント ハンドラーの作成
現在、EditItemTemplate には databinding 構文がありません (UnitPriceLabel から文字どおりにコピーされた ItemTemplate を除く)。 databinding 構文はすぐに追加しますが、最初に DataList の EditCommand と CancelCommand イベントのイベント ハンドラーを作成します。
EditCommand イベント ハンドラーの役割は、[Edit] ボタンがクリックされた DataList 項目の編集インターフェイスをレンダリングすることであり、一方、CancelCommand のジョブは DataList を編集前の状態に戻すことであることを思い出してください。
これら 2 つのイベント ハンドラーを作成し、次のコードを使います。
protected void Products_EditCommand(object source, DataListCommandEventArgs e)
{
// Set the DataList's EditItemIndex property and rebind the data
Products.EditItemIndex = e.Item.ItemIndex;
Products.DataBind();
}
protected void Products_CancelCommand(object source, DataListCommandEventArgs e)
{
// Return to DataList to its pre-editing state
Products.EditItemIndex = -1;
Products.DataBind();
}
これら 2 つのイベント ハンドラーが配置された状態で、[Edit] ボタンをクリックすると編集インターフェイスが表示され、[Cancel] ボタンをクリックすると、編集した項目が読み取り専用モードに戻ります。 図 8 は、Chef Anton の Gumbo Mix の [Edit] ボタンをクリックした後の DataList を示しています。 編集インターフェイスに databinding 構文をまだ追加していないため、ProductName TextBox は空白、Discontinued CheckBox はオフになっており、最初の項目は Categories と Suppliers DropDownList から選ばれています。
図 8: [Edit] ボタンをクリックすると編集インターフェイスが表示されます (クリックするとフルサイズの画像が表示されます)
手順 4: DataBinding 構文を編集インターフェイスに追加する
編集インターフェイスに現在の製品の値を表示させるには、databinding 構文を使って、データ フィールドの値を適切な Web コントロールの値に割り当てる必要があります。 databinding 構文は、デザイナーで [テンプレートの編集] 画面に移動し、Web コントロールのスマート タグから [DataBindings の編集] リンクを選ぶことで適用できます。 または、databinding 構文を宣言型マークアップに直接追加することもできます。
ProductName データ フィールド値を ProductName TextBox の Text プロパティに、CategoryID および SupplierID データ フィールド値を Categories および Suppliers DropDownList の SelectedValue プロパティに、Discontinued データ フィールドの値を Discontinued CheckBox の Checked プロパティに割り当てます。 デザイナーを通じて、または直接、宣言的マークアップを使ってこれらの変更を行った後、ブラウザーでページに再度アクセスし、Chef Anton の Gumbo Mix の [Edit] ボタンをクリックします。 図 9 に示すように、databinding 構文により、現在の値が TextBox、DropDownList、CheckBox に追加されています。
図 9: [Edit] ボタンをクリックすると編集インターフェイスが表示されます (クリックするとフルサイズの画像が表示されます)
手順 5: ユーザーの変更を UpdateCommand イベント ハンドラーに保存する
ユーザーが製品を編集して [Update] ボタンをクリックすると、ポストバックが発生し、DataList の UpdateCommand イベントが発生します。 イベント ハンドラーでは、EditItemTemplate の Web コントロールから値を読み取り、BLL とインターフェイスをとってデータベース内の製品を更新する必要があります。 前のチュートリアルで説明したように、更新された製品の ProductID には、DataKeys コレクションを通じてアクセスできます。 ユーザー入力フィールドには、次のコードに示すように、FindControl("controlID") を使ってプログラムで Web コントロールを参照することによってアクセスします。
protected void Products_UpdateCommand(object source, DataListCommandEventArgs e)
{
// Make sure the page is valid...
if (!Page.IsValid)
return;
// Read in the ProductID from the DataKeys collection
int productID = Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]);
// Read in the product name and price values
TextBox productName = (TextBox)e.Item.FindControl("ProductName");
DropDownList categories = (DropDownList)e.Item.FindControl("Categories");
DropDownList suppliers = (DropDownList)e.Item.FindControl("Suppliers");
CheckBox discontinued = (CheckBox)e.Item.FindControl("Discontinued");
string productNameValue = null;
if (productName.Text.Trim().Length > 0)
productNameValue = productName.Text.Trim();
int categoryIDValue = Convert.ToInt32(categories.SelectedValue);
int supplierIDValue = Convert.ToInt32(suppliers.SelectedValue);
bool discontinuedValue = discontinued.Checked;
// Call the ProductsBLL's UpdateProduct method...
ProductsBLL productsAPI = new ProductsBLL();
productsAPI.UpdateProduct(productNameValue, categoryIDValue, supplierIDValue,
discontinuedValue, productID);
// Revert the DataList back to its pre-editing state
Products.EditItemIndex = -1;
Products.DataBind();
}
このコードは、まず Page.IsValid プロパティを参照して、ページ上のすべての検証コントロールが有効であることを確認します。
Page.IsValid が True の場合、編集された商品の ProductID 値が DataKeys コレクションから読み取られ、EditItemTemplate 内のデータ入力 Web コントロールがプログラムによって参照されます。 次に、これらの Web コントロールの値が変数に読み取られてから、適切な UpdateProduct オーバーロードに渡されます。 データ更新後、DataList は編集前の状態に戻ります。
注
コードとこの例に焦点を当てるため、BLL レベルと DAL レベルの例外を処理するチュートリアルで追加された例外処理ロジックは省略しました。 このチュートリアルを完了した後、演習としてこの機能を追加します。
手順 6: NULL CategoryID と SupplierID 値の処理
Northwind データベースでは、NULL テーブルの Products 列と CategoryID 列に SupplierID 値を使用できます。 ただし、編集インターフェイスは、現在、NULL 値に対応していません。
NULL 列または CategoryID 列のいずれかに SupplierID 値を持つ製品を編集しようとすると、ArgumentOutOfRangeException が次のようなエラー メッセージで表示されます: "'Categories' に、項目の一覧に存在しないため無効な SelectedValue があります。"また、現時点では、製品のカテゴリまたは仕入先の値を NULL 以外の値から NULL 値に変更する方法はありません。
カテゴリと仕入先の DropDownList の NULL 値をサポートするには、追加の ListItem を追加する必要があります。 この Text の ListItem 値として (None) を使うことを選びましたが、必要に応じて他の値 (空の文字列など) に変更できます。 最後に、確実に DropDownLists AppendDataBoundItems を True に設定してください。これを忘れると、DropDownList にバインドされたカテゴリと仕入先によって、静的に追加された ListItem が上書きされます。
これらの変更を行った後、DataList の EditItemTemplate 内の DropDownList のマークアップは次のようになります。
<asp:DropDownList ID="Categories" DataSourceID="CategoriesDataSource"
DataTextField="CategoryName" DataValueField="CategoryID" runat="server"
SelectedValue='<%# Eval("CategoryID") %>' AppendDataBoundItems="True">
<asp:ListItem Value=" Selected="True">(None)</asp:ListItem>
</asp:DropDownList>
...
<asp:DropDownList ID="Suppliers" DataSourceID="SuppliersDataSource"
DataTextField="CompanyName" DataValueField="SupplierID" runat="server"
SelectedValue='<%# Eval("SupplierID") %>' AppendDataBoundItems="True">
<asp:ListItem Value=" Selected="True">(None)</asp:ListItem>
</asp:DropDownList>
注
静的 ListItem は、デザイナーを通じて、または直接、宣言構文を使って DropDownList に追加できます。 データベースの NULL 値を表す DropDownList 項目を追加する場合は、必ず宣言構文を使って ListItem を追加します。 デザイナーで ListItem コレクション エディターを使う場合、空の文字列が割り当てられた場合、生成された宣言構文では Value 設定が完全に省略され、<asp:ListItem>(None)</asp:ListItem> のような宣言型マークアップが作成されます。 これは無害に見えるかもしれませんが、Value が欠落しているため、DropDownList では代わりに Text プロパティ値が使われます。 つまり、この NULLListItem が選択されると、値 (なし) が製品データ フィールド (このチュートリアルでは CategoryIDまたは SupplierID) に割り当てられようとし、例外が発生します。
Value=""を明示的に設定すると、 NULLNULL が選択されたときに製品データ フィールドに ListItem の値が割り当てられます。
少し時間を取って、ブラウザーで進捗を確認してみます。 製品を編集するときは、Categories と Suppliers の両方の DropDownList で、(None) オプションが DropDownList の先頭にあることに注意してください。
図 10: Categories と Suppliers DropDownList には (None) オプションが含まれています (クリックするとフルサイズの画像が表示されます)
(None) オプションをデータベースの NULL 値として保存するには、UpdateCommand イベント ハンドラーに戻る必要があります。 DropDownList の categoryIDValue が空の文字列でない場合にのみ、supplierIDValue 変数と Nothing 変数を Null 許容の整数に変更し、SelectedValue 以外の値を割り当てます。
int? categoryIDValue = null;
if (!string.IsNullOrEmpty(categories.SelectedValue))
categoryIDValue = Convert.ToInt32(categories.SelectedValue);
int? supplierIDValue = null;
if (!string.IsNullOrEmpty(suppliers.SelectedValue))
supplierIDValue = Convert.ToInt32(suppliers.SelectedValue);
この変更により、ユーザーがいずれかドロップダウン リストから (None) オプションを選んだ場合、データベース値 Nothing に対応する値 UpdateProduct が NULL BLL メソッドに渡されます。
まとめ
このチュートリアルでは、TextBox、2 つの DropDownList、CheckBox という 3 つの異なる入力 Web コントロールと検証コントロールを含む、より複雑な DataList 編集インターフェイスを作成する方法について説明しました。 編集インターフェイスを構築する場合、使う Web コントロールに関係なく手順は同じです。まず、Web コントロールを DataList の EditItemTemplate に追加します。databinding 構文を使って、対応するデータ フィールド値を適切な Web コントロール プロパティに割り当てます。そして、UpdateCommand イベント ハンドラーで、プログラムで Web コントロールとその適切なプロパティにアクセスし、その値を BLL に渡します。
編集インターフェイスを作成するときは、それが TextBox だけで構成されている場合でも、さまざまな Web コントロールのコレクションでも、データベースの NULL 値を正しく処理するようにします。
NULL を考慮する場合、編集インターフェイスで既存の NULL 値を適切に表示するだけでなく、値を NULL としてマークする手段を提供することが不可欠です。 DataList の DropDownList の場合、通常は、ListItem プロパティが空の文字列 (Value) に明示的に設定されている静的 Value="" を追加し、UpdateCommand イベント ハンドラーに少しコードを追加して、NULL``ListItem が選ばれたかどうかを判断します。
プログラミングに満足!
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを使用しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の著書は Sams Teach Yourself ASP.NET 2.0 in 24 Hoursです。 彼には mitchell@4GuysFromRolla.comで連絡できます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Dennis Patterson、David Suru、Randy Schmidt でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.comにメッセージを送ってください。