削除時、クライアント側の確認を追加する (C#)
これまでに作成したインターフェイス内では、ユーザーが [編集] ボタンをクリックするつもりで [削除] ボタンをクリックしてしまい、誤ってデータを削除するおそれがあります。 このチュートリアルでは、[削除] ボタンをクリックした際に表示される、クライアント側での確認ダイアログ ボックスを追加します。
はじめに
過去のいくつかのチュートリアルでは、アプリケーション アーキテクチャ、ObjectDataSource、データ Web コントロールを組み合わせて、挿入、編集、削除の機能を提供する方法について確認しました。 これまでに検討した削除インターフェイスは、[削除] ボタンで構成されています。このボタンをクリックするとポストバックが発生し、ObjectDataSource の Delete()
メソッドが呼び出されます。 次に、この Delete()
メソッドは構成されたメソッドをビジネス ロジック層から呼び出し、その呼び出しをデータ アクセス層に伝達して、実際の DELETE
ステートメントをデータベースに発行します。
このユーザー インターフェイスを使用すると、訪問者は GridView、DetailsView、または FormView コントロールを使用してレコードを削除することができますが、ユーザーが [削除] ボタンをクリックしても何も確認はありません。 ユーザーが [編集] をクリックするつもりで誤って [削除] ボタンをクリックした場合、更新するはずだったレコードは代わりに削除されてしまいます。 これを防ぐために、このチュートリアルでは [削除] ボタンをクリックした際に表示されるクライアント側での確認ダイアログ ボックスを追加します。
JavaScript の confirm(string)
関数は、文字列の入力パラメーターを、[OK] と [キャンセル] ボタン (図 1 を参照) の 2 つのボタンを備えたモーダル ダイアログ ボックス内のテキストとして表示します。 この confirm(string)
関数は、クリックされるボタンに応じてブール値 (ユーザーが [OK] をクリックした場合は true
、そして [キャンセル] をクリックした場合は false
) を返します。
図 1: JavaScript の confirm(string)
メソッドでクライアント側のモーダル メッセージ ボックスを表示する
フォームの送信中に、クライアント側のイベント ハンドラーから false
値が返された場合、このフォームの送信は取り消されます。 この機能を使用して、[削除] ボタンのクライアント側 onclick
イベント ハンドラーに、confirm("Are you sure you want to delete this product?")
の呼び出しの値を返させることができます。 ユーザーが [キャンセル] をクリックすると confirm(string)
は false を返し、このフォームの送信が取り消されます。 ポストバックがなければ、[削除] ボタンがクリックされた製品は削除されません。 ただし、ユーザーが確認ダイアログ ボックス内で [OK] をクリックした場合は、ポストバックがそのまま続行されて、製品は削除されます。 この手法の詳細については、JavaScript の confirm()
メソッドを使用したフォーム送信の制御を参照してください。
必要なクライアント側スクリプトの追加は、テンプレートを使用する場合と、CommandField を使用する場合で若干異なります。 したがって、このチュートリアルでは FormView と GridView の両方の例を見ていきます。
Note
このチュートリアルで説明したもののように、クライアント側での確認手法を使用することは、ユーザーが JavaScript をサポートするブラウザーを使用してアクセスしており、JavaScript が有効であることを前提としています。 これらの前提条件のいずれかが特定のユーザーに当てはまらない場合は、[削除] ボタンをクリックするとすぐにポストバックが発生します (確認メッセージ ボックスは表示されません)。
手順 1: 削除をサポートする FormView の作成
まず、EditInsertDelete
フォルダー内の ConfirmationOnDelete.aspx
ページに FormView を追加し、ProductsBLL
クラスの GetProducts()
メソッドを使用して製品情報を取得する新しい ObjectDataSource にそれをバインドします。 また、ProductsBLL
クラスの DeleteProduct(productID)
メソッドが ObjectDataSource の Delete()
メソッドにマップされるように ObjectDataSource を構成します。[INSERT] タブと [UPDATE] タブのドロップダウン リストを確実に [(なし)] に設定してください。 最後に、FormView のスマート タグ内の [ページングを有効にする] チェック ボックスをオンにします。
これらの手順の後、新しい ObjectDataSource の宣言型マークアップは次のようになります。
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
DeleteMethod="DeleteProduct" OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
<DeleteParameters>
<asp:Parameter Name="productID" Type="Int32" />
</DeleteParameters>
</asp:ObjectDataSource>
オプティミスティック同時実行制御を使用しなかった過去の例と同様に、少し時間をとって ObjectDataSource の OldValuesParameterFormatString
プロパティをクリアしてください。
削除のみをサポートする ObjectDataSource コントロールにバインドされているため、この FormView の ItemTemplate
には [削除] ボタンのみが提供され、[新規] ボタンと [更新] ボタンはありません。 ただし、FormView の宣言型マークアップには余分な EditItemTemplate
と InsertItemTemplate
が含まれています (それらは削除して構いません)。 少し時間をとって、製品データ フィールドのサブセットのみを表示するように ItemTemplate
をカスタマイズします。 そのサプライヤーとカテゴリ名の上の <h3>
見出し内に、製品名を ([削除] ボタンと一緒に) 表示するように構成しました。
<asp:FormView ID="FormView1" AllowPaging="True" DataKeyNames="ProductID"
DataSourceID="ObjectDataSource1" runat="server">
<ItemTemplate>
<h3><i><%# Eval("ProductName") %></i></h3>
<b>Category:</b>
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Eval("CategoryName") %>'>
</asp:Label><br />
<b>Supplier:</b>
<asp:Label ID="SupplierNameLabel" runat="server"
Text='<%# Eval("SupplierName") %>'>
</asp:Label><br />
<asp:LinkButton ID="DeleteButton" runat="server" CausesValidation="False"
CommandName="Delete" Text="Delete">
</asp:LinkButton>
</ItemTemplate>
</asp:FormView>
これらの変更により、ユーザーが一度に 1 つずつ製品を切り替え、ただ [削除] ボタンをクリックするだけで製品を削除できる機能を備えた、すべてが機能する Web ページを用意しました。 図 2 は、ブラウザーを使用して表示した場合の、これまでの経過のスクリーン ショットを示しています。
図 2: FormView は 1 つの製品に関する情報を示しています (クリックするとフルサイズの画像が表示されます)
手順 2: [削除] ボタンのクライアント側 onclick イベントからの confirm(string) 関数の呼び出し
FormView を作成したら、最後の手順は、訪問者がクリックした際に JavaScript の confirm(string)
関数を呼び出すように [削除] ボタンを構成することです。 Button、LinkButton、または ImageButton のクライアント側 onclick
イベントへのクライアント側スクリプトの追加は、ASP.NET 2.0 の新機能である OnClientClick property
を使用することで実現できます。 confirm(string)
関数の値を返すようにするので、このプロパティを単純に次のように設定します。return confirm('Are you certain that you want to delete this product?');
この変更後、Delete LinkButton の宣言構文は次のようになります。
<asp:LinkButton ID="DeleteButton" runat="server" CausesValidation="False"
CommandName="Delete" Text="Delete"
OnClientClick="return confirm('Are you certain you want to delete this product?');">
</asp:LinkButton>
以上で終了です。 図 3 に、この確認の動作のスクリーン ショットを示します。 [削除] ボタンをクリックすると、確認のダイアログ ボックスが表示されます。 ユーザーが [キャンセル] をクリックした場合、ポストバックは取り消され、製品は削除されません。 ただし、ユーザーが [OK] をクリックするとポストバックは続行し、ObjectDataSource の Delete()
メソッドが呼び出され、データベース レコードが削除されます。
Note
confirm(string)
JavaScript 関数に渡される文字列は、(引用符ではなく) アポストロフィで区切られます。 JavaScript では、文字列はいずれかの文字を使用して区切ることができます。 ここでは confirm(string)
に渡す文字列の区切り記号にアポストロフィを使用して、OnClientClick
のプロパティ値に使用される区切り記号があいまいにならないようにします。
図 3: [削除] ボタンをクリックすると確認が表示されるようになりました (クリックするとフルサイズの画像が表示されます)
手順 3: CommandField 内の [削除] ボタンの OnClientClick プロパティの構成
テンプレート内で Button、LinkButton、または ImageButton を直接操作する場合は、JavaScript の confirm(string)
関数の結果を返すようにその OnClientClick
プロパティを構成するだけで、確認のダイアログ ボックスを関連付けることができます。 ただし、GridView または DetailsView に [削除] ボタンのフィールドを追加する CommandField には、宣言によって設定できる OnClientClick
プロパティがありません。 代わりに、GridView または DetailsView の適切な DataBound
イベント ハンドラー内の [削除] ボタンをプログラムで参照し、そこでその OnClientClick
プロパティを設定する必要があります。
Note
適切な DataBound
イベント ハンドラー内で [削除] ボタンの OnClientClick
プロパティを設定すると、現在のレコードにバインドされたデータにアクセスできます。 これは、確認メッセージを拡張して、たとえば "インド風の紅茶製品を削除してもよろしいですか?" といった、特定のレコードに関する詳細を含められることを意味します。このようなカスタマイズは、データ バインド構文を使用するテンプレート内でも可能です。
CommandField 内の [削除] ボタンの OnClientClick
プロパティの設定を実践するために、ページに GridView を追加しましょう。 FormView で使用するのと同じ ObjectDataSource コントロールを使用するように、この GridView を構成します。 また、GridView の BoundField を制限し、製品名、カテゴリ、サプライヤーのみを含めるようにします。 最後に、GridView のスマート タグから [削除を有効にする] チェック ボックスをオンにします。 これにより、その ShowDeleteButton
プロパティに true
が設定された CommandField が、GridView の Columns
コレクションに追加されます。
これらの変更を加えた後、この GridView の宣言型マークアップは次のようになります。
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ObjectDataSource1">
<Columns>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True"
SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier" ReadOnly="True"
SortExpression="SupplierName" />
</Columns>
</asp:GridView>
CommandField には、GridView の RowDataBound
イベント ハンドラーからプログラムでアクセスできる、1 つの Delete LinkButton インスタンスが含まれています。 参照されると、それに応じてその OnClientClick
プロパティを設定できます。 次のコードを使用して、RowDataBound
イベントのイベント ハンドラーを作成します。
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// reference the Delete LinkButton
LinkButton db = (LinkButton)e.Row.Cells[0].Controls[0];
// Get information about the product bound to the row
Northwind.ProductsRow product =
(Northwind.ProductsRow) ((System.Data.DataRowView) e.Row.DataItem).Row;
db.OnClientClick = string.Format(
"return confirm('Are you certain you want to delete the {0} product?');",
product.ProductName.Replace("'", @"\'"));
}
}
このイベント ハンドラーはデータ行 ([削除] ボタンを持つ行) を操作し、まずプログラムで [削除] ボタンを参照します。 一般に、次のパターンを使用します。
ButtonType obj = (ButtonType) e.Row.Cells[commandFieldIndex].Controls[controlIndex];
ButtonType は、CommandField で使用されるボタンの種類です (Button、LinkButton、または ImageButton)。 既定では、CommandField では LinkButton が使用されますが、これは CommandField の ButtonType property
を使用してカスタマイズできます。 commandFieldIndex は、GridView の Columns
コレクション内の CommandField の序数インデックスです。一方、controlIndex は、CommandField の Controls
コレクション内の [削除] ボタンのインデックスです。 controlIndex の値は、CommandField 内の他のボタンに対するこのボタンの位置によって異なります。 たとえば、CommandField 内で表示される唯一のボタンが [削除] ボタンの場合は、インデックス 0 を使用します。 しかし、[削除] ボタンの前に [編集] ボタンがある場合は、インデックス 2 を使用します。 インデックス 2 を使用する理由は、[削除] ボタンの前に CommandField によって 2 つのコントロールが追加されるためです。つまり、[編集] ボタンと LiteralControl ([編集] ボタンと [削除] ボタンの間にいくらかのスペースを追加するために使用されます) です。
この特定の例では、CommandField に LinkButton が使用され、左端のフィールドの commandFieldIndex は 0 です。 CommandField 内に [削除] ボタン以外のボタンはないため、controlIndex では 0 が使用されます。
CommandField 内の [削除] ボタンを参照した後、次に現在の GridView 行にバインドされている製品に関する情報を取得します。 最後に、[削除] ボタンの OnClientClick
プロパティを、製品名を含む適切な JavaScript に設定します。 confirm(string)
関数に渡される JavaScript の文字列はアポストロフィを使用して区切られるため、製品名内に現れるアポストロフィをエスケープする必要があります。 特に、製品名内のアポストロフィは "\'
" でエスケープします。
これらの変更が完了し、GridView 内の [削除] ボタンをクリックすると、カスタマイズした確認ダイアログ ボックスが表示されます (図 4 を参照)。 FormView の確認メッセージ ボックスと同様に、ユーザーが [キャンセル] をクリックするとポストバックは取り消され、削除が行われません。
Note
この手法は、DetailsView 内の CommandField 内にある [削除] ボタンにプログラムでアクセスするためにも使用できます。 ただし、DetailsView の場合、DetailsView には RowDataBound
イベントがないため、DataBound
イベントのイベント ハンドラーを作成します。
図 4: GridView の [削除] ボタンをクリックすると、カスタマイズした確認ダイアログ ボックスが表示されます (クリックするとフルサイズの画像が表示されます)
TemplateField の使用
CommandField の欠点の 1 つは、そのボタンにインデックスを使用してアクセスし、結果のオブジェクトを適切なボタンの種類 (Button、LinkButton、または ImageButton) にキャストする必要があることです。 "マジックナンバー" とハードコーディングされた型を使用すると、実行時まで検出できない問題を招きます。 たとえば、あなたまたは別の開発者が、将来のある時点で CommandField に新しいボタン ([編集] ボタンなど) を追加したり、ButtonType
プロパティを変更したりした場合、既存のコードは引き続きエラーなしでコンパイルされますが、そのページにアクセスすると、コードの記述方法や変更内容に応じて、例外または予期せぬ動作が発生するおそれがあります。
別のアプローチでは、GridView と DetailsView の CommandField を TemplateField に変換します。 これにより、CommandField 内の各ボタン用に LinkButton (または Button または ImageButton) を持つ ItemTemplate
を備えた TemplateField が生成されます。 これらのボタンの OnClientClick
プロパティは、FormView で確認したように、宣言で割り当てることができます。または、次のパターンを使用して、適切な DataBound
イベント ハンドラー内でプログラムでアクセスすることができます。
ButtonType obj = (ButtonType) e.Row.FindControl("controlID");
controlID の箇所はボタンの ID
プロパティの値です。 このパターンでも、キャストするにはハードコーディングされた型が必要ですが、インデックス作成が不要になり、実行時のエラーを発生させずにレイアウトを変更できます。
まとめ
JavaScript の confirm(string)
関数は、フォーム送信のワークフローを制御するために一般的に使用される手法です。 この関数を実行すると、2 つのボタン ([OK] と [キャンセル]) を含むクライアント側のモーダル ダイアログ ボックスが表示されます。 ユーザーが [OK] をクリックすると、confirm(string)
関数は true
を返します。[キャンセル] をクリックすると false
を返します。 この機能は、送信プロセス中にイベント ハンドラーが false
を返した場合にフォームの送信を取り消すブラウザーの動作と組み合わせて、レコードを削除する際に確認メッセージ ボックスを表示するために使用できます。
confirm(string)
関数はコントロールの OnClientClick
プロパティを使用して、Button Web コントロールのクライアント側 onclick
イベント ハンドラーに関連付けることができます。 FormView のテンプレートの 1 つ、または DetailsView か GridView 内の TemplateField 内でテンプレート内の [削除] ボタンを操作する場合、このプロパティは、このチュートリアルで確認したように、宣言またはプログラムによって設定できます。
プログラミングに満足!
著者について
Scott Mitchell は、7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者です。1998 年から Microsoft の Web テクノロジに携わっています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。