DataList の編集インターフェイスをカスタマイズする (C#)
このチュートリアルでは、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 の編集可能なインターフェイスを作成する前に、まず読み取り専用インターフェイスを構築する必要があります。 まず、EditDeleteDataList
フォルダーから CustomizedUI.aspx
ページを開き、デザイナーから DataList をページに追加して、その ID
プロパティを Products
に設定します。 DataList のスマート タグから、新しい ObjectDataSource を作成します。 この新しい ObjectDataSource ProductsDataSource
に名前を付け、ProductsBLL
クラスの GetProducts
メソッドからデータを取得するように構成します。 以前の編集可能な DataList チュートリアルと同様に、ビジネス ロジック レイヤーに直接移動して、編集した製品の情報を更新します。 そのため、[UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを (None) に設定します。
図 2: [UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを (None) に設定します (クリックするとフルサイズの画像が表示されます)
ObjectDataSource を構成すると、Visual Studio によって、返された各データ フィールドの名前と値を一覧表示する DataList の既定の ItemTemplate
が作成されます。 テンプレートの <h4>
要素に製品名がカテゴリ名、仕入先名、価格、生産中止状態と共に一覧表示されるように、ItemTemplate
を変更します。 さらに、[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>
を使って製品情報をレイアウトします。 Styles.css
で定義されている ProductPropertyLabel
および ProductPropertyValue
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 構文がありません (ItemTemplate
から文字どおりにコピーされた UnitPriceLabel
を除く)。 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 は編集前の状態に戻ります。
Note
コードとこの例に焦点を当てるため、BLL レベルと DAL レベルの例外を処理するチュートリアルで追加された例外処理ロジックは省略しました。 このチュートリアルを完了した後、演習としてこの機能を追加します。
手順 6: NULL CategoryID と SupplierID 値の処理
Northwind データベースでは、Products
テーブルの CategoryID
列と SupplierID
列に NULL
値を使用できます。 ただし、編集インターフェイスは、現在、NULL
値に対応していません。 CategoryID
列または SupplierID
列のいずれかに NULL
値を持つ製品を編集しようとすると、ArgumentOutOfRangeException
が次のようなエラー メッセージで表示されます: "'Categories' に、項目の一覧に存在しないため無効な SelectedValue があります。"また、現時点では、製品のカテゴリまたは仕入先の値を NULL
以外の値から NULL
値に変更する方法はありません。
カテゴリと仕入先の DropDownList の NULL
値をサポートするには、追加の ListItem
を追加する必要があります。 この ListItem
の Text
値として (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>
Note
静的 ListItem
は、デザイナーを通じて、または直接、宣言構文を使って DropDownList に追加できます。 データベースの NULL
値を表す DropDownList 項目を追加する場合は、必ず宣言構文を使って ListItem
を追加します。 デザイナーで ListItem
コレクション エディターを使う場合、空の文字列が割り当てられた場合、生成された宣言構文では Value
設定が完全に省略され、<asp:ListItem>(None)</asp:ListItem>
のような宣言型マークアップが作成されます。 これは無害に見えるかもしれませんが、Value
が欠落しているため、DropDownList では代わりに Text
プロパティ値が使われます。 つまり、この NULL
ListItem
を選択すると、値 (None) が製品データ フィールド (このチュートリアルではCategoryID
または SupplierID
) に割り当てられ、例外が発生します。 Value=""
を明示的に設定すると、NULL
ListItem
が選択されたときに、NULL
値が製品データ フィールドに割り当てられます。
少し時間を取って、ブラウザーで進捗を確認してみます。 製品を編集するときは、Categories
と Suppliers
の両方の DropDownList で、(None) オプションが DropDownList の先頭にあることに注意してください。
図 10: Categories
と Suppliers
DropDownList には (None) オプションが含まれています (クリックするとフルサイズの画像が表示されます)
(None) オプションをデータベースの NULL
値として保存するには、UpdateCommand
イベント ハンドラーに戻る必要があります。 DropDownList の SelectedValue
が空の文字列でない場合にのみ、categoryIDValue
変数と supplierIDValue
変数を Null 許容の整数に変更し、Nothing
以外の値を割り当てます。
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) オプションを選んだ場合、データベース値 NULL
に対応する値 Nothing
が UpdateProduct
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 の場合、通常は、Value
プロパティが空の文字列 (Value=""
) に明示的に設定されている静的 ListItem
を追加し、UpdateCommand
イベント ハンドラーに少しコードを追加して、NULL``ListItem
が選ばれたかどうかを判断します。
プログラミングに満足!
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Dennis Patterson、David Suru、Randy Schmidt でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。