GridView グリッド内のインライン編集
ここでは、データ編集を非常に簡単に実装可能とする GridView のインライン編集機能について説明します。
SqlDataSource も利用しますので、まだの人は「GridView と SqlDataSource コントロールのバインド」にも目を通しておくと良いと思います。
さて、さっそくここで実装するプログラムの動作を説明します。
まず、データベースに次のようなテーブル (テーブル名 Users) を作成します。
名前とメールアドレスが登録されるテーブルです。UserID は INT 型で IDENTITY (オートインクリメント) のプライマリキーです。
このテーブルと接続する SqlDataSource オブジェクトを作成し、GridView にバインドすると次のようにテーブルの内容が表示されます。
プライマリキーの UserID フィールドは表示していません。
さて、このテーブルの内容を簡単に編集するにはどうしたら良いでしょうか。
行毎に「Edit」(編集) ボタンがついています。このボタンをクリックすると、クリックされたボタンと同じ行のメールアドレスカラムが、 編集モードに入ります。
ここでは名前は編集不可として、メールアドレスだけを編集可能にしています。
メールアドレスを編集し、[Save] (保存) ボタンをクリックします。
すると、編集されたメールアドレスが表示されました。
データベース (ここでは SQL Server) の内容を、SQL Server の管理ツールからみると、確かにデータが更新されていることがわかります。
以上でレコードの更新ができました。
さらに続けて、[Edit] ボタンをクリックして表示される [Delete] (削除) ボタンをクリックしてみます。
すると直ちに削除ボタンをクリックした行のデータが削除されます。
データベースを確認しても確かにレコードが消えています。
以上で、レコードの削除もできました。
今回説明するのはこの動きです。グリッドの行内で直ちに編集したり、レコードを削除したりできます。これをインライン編集と呼びます。
実は GridView を使うとこのような動作は、C# (もしくは VB.NET などの) コードを全く記述しなくても実現することが可能です。
GridView と SqlDataSource を利用したインライン編集
上記のコードを実現する aspx コードは次のようになります。
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="GridViewTest1.aspx.cs" Inherits="GridViewTest1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>GridView Test</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView
ID="GridView1"
runat="server"
DataKeyNames="UserID"
AutoGenerateColumns="False"
DataSourceID="SqlDataSource1"
ShowHeaderWhenEmpty="True">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" ReadOnly="true" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" ReadOnly="true" />
<asp:BoundField DataField="Email" HeaderText="Email" />
<asp:TemplateField HeaderText="#">
<ItemTemplate>
<asp:Button runat="server" CommandName="Edit" Text="Edit" />
</ItemTemplate>
<EditItemTemplate>
<asp:Button runat="server" CommandName="Update" Text="Save" />
<asp:Button runat="server" CommandName="Cancel" Text="Cancel" />
<asp:Button runat="server" CommandName="Delete" Text="Delete" />
</EditItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:SqlDataSource
ID="SqlDataSource1"
runat="server"
ConnectionString="<%$ ConnectionStrings:DB1 %>"
UpdateCommand="UPDATE Users SET Email=@Email WHERE UserID=@UserID"
DeleteCommand="DELETE FROM Users WHERE UserID=@UserID"
SelectCommand="SELECT * FROM Users"></asp:SqlDataSource>
</div>
</form>
</body>
</html>
順を追って説明します。
GridView の属性は次のように設定されています。
...
<asp:GridView
ID="GridView1"
runat="server"
DataKeyNames="UserID"
AutoGenerateColumns="False"
DataSourceID="SqlDataSource1"
ShowHeaderWhenEmpty="True">
...
DataKeyNames は行を識別するためのキーとなるフィールドを指定します。ここでは UserID がプライマリキーなので、それを記載しています。
データソースとなる SqlDataSource オブジェクトの ID は SqlDataSource1 です。これは GridView 要素の下に書いてあります。詳しくは後述しますが、要は Users テーブルを読み込んでいます。このため GridView は Users テーブル内のフィールドを認識できます。
ここで AutoGenerateColumns を False にしているので、GridView に表示するカラムをひとつひとつ記述する必要があります。それを書いたのが Columns 要素内です。
...
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" ReadOnly="true" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" ReadOnly="true" />
<asp:BoundField DataField="Email" HeaderText="Email" />
<asp:TemplateField HeaderText="#">
<ItemTemplate>
<asp:Button runat="server" CommandName="Edit" Text="Edit" />
</ItemTemplate>
<EditItemTemplate>
<asp:Button runat="server" CommandName="Update" Text="Save" />
<asp:Button runat="server" CommandName="Cancel" Text="Cancel" />
<asp:Button runat="server" CommandName="Delete" Text="Delete" />
</EditItemTemplate>
</asp:TemplateField>
</Columns>
...
まずは BoundField でデータベースのカラムの内容を表示します。 FirstName と LastName は編集不可とするので、ReadOnly 属性を True にします。これによって、GridView が編集モードになっても、 編集ボックスが表示されません。
逆に Email とバウンド (結合) している BoundField は ReadOnly を設定していません。デフォルトで ReadOnly ではありません。
次に、編集を開始するためのボタンを表示するカラムを作成しています。
TemplateField として1カラム用意して、その中に ItemTemplate と EditItemTemplate の二つを用意します。 前者は読み取り専用モードで使用され、後者が編集モードに入ったときに使われます。
通常は [Edit] ボタン一つを表示していて、編集モードに入ったら [Save] [Cancel] [Delete] の三つのボタンを表示します。
このため ItemTemplate 内には Button が一つ。EditItemTemplate にはボタンが三つ記述されています。
さて、どのボタンを押したときに、何が起きるのか、どうやって決めているのでしょうか。ポイントは CommandName 属性にあります。
CommandName には文字列を自由に設定できるのですが、実はいくつか決まりきったキーワードが存在しています。
"Edit"、"Update"、"Cancel"、"Delete" などはそのキーワードの一つです。
CommandName が Edit のボタンを押すと、Edit その行を編集モードにして、RowEditing イベントを発生させます。
CommandName が Update のボタンを押すと、関連付けされたデータソースの Update コマンドを実行します。また、そのコマンド実行前後に、 RowUpdating イベントと RowUpdated イベントを発生させます。
CommandName が Cancel のボタンを押すと、その行を読み取り専用モードにして、RowCancelingEdit イベントを発生させます。
CommandName が Delete イベントのボタンを押すと、関連付けされたデータソースの Delete コマンドを実行します。また、そのコマンド実行前後に RowDeleting イベントと RowDeleted イベントを発生させます。
ここではイベントハンドラは何も設定していませんので、Update ボタンで SqlDataSource の Update コマンドを、 Delete ボタンで Delete コマンドをそれぞれ直ちに実行します。
SqlDataSource のコマンド定義
さて、コマンドを実行するといっても、SqlDataSource にはどうやってパラメータを渡すのしょうか?
SqlDataSource のコマンドをみてみましょう。
...
<asp:SqlDataSource
ID="SqlDataSource1"
runat="server"
ConnectionString="<%$ ConnectionStrings:DB1 %>"
UpdateCommand="UPDATE Users SET Email=@Email WHERE UserID=@UserID"
DeleteCommand="DELETE FROM Users WHERE UserID=@UserID"
SelectCommand="SELECT * FROM Users"></asp:SqlDataSource>
</div>
...
@ マークで記述されているところがパラメータになります。このパラメータはバウンドフィールドの DataField 属性で定義された要素の値が自動的に入ります。
ここでは更新されるデータは Email ですが、Update コマンドでは Email フィールドを更新する値として、@Email が使えます。
問題は、行を識別するキーをどのように渡すかです。今回は行を識別するのは UserID カラムですが、UserID カラムは BoundField では接続していません。 このため、このままでは @UserID というパラメータが使えないことになります。このため GridView の DataKeyNames にキーとなる UserID カラム名を含めます。これによって、@UserID というパラメータが使えるようにしてあります。
以上で、ボタンを押したときに行われる動作および渡すパラメータが全てわかりましたね。
より簡単にするために
ちなみに、実は上記のようなボタンであれば、CommandField を使えば似たコードがより簡単に記述できます。
次のように Edit ボタンを表示 (ShowEditButton = True)、Cancel ボタンを表示 (ShowCancelButton = True)、Delete ボタンを表示 (ShowDeleteButton = True) とします。
すると、次のようにボタンが出現します。
Delete ボタンが最初から表示されている所は違いますが、ボタンを押した後の動作は Edit、Update などそれぞれ同様です。
あまりに単純化されているとあっという間に全て出来上がってしまい、何が起きたかわからなくなったり、あるいは応用がきかず、 「簡単なのはいいけど、柔軟性が無いよね」などの評価になってしまうと思ったので、最初に裏の仕組みが分かるような記述にしてみました。
GridView は決まりきったことなら今回みたように非常に簡単に実現できますし、さらに一歩踏み込んでカスタマイズするときも、 TemplateField を使えばかなり柔軟にいろいろできますので、大変便利です。