それでは、[編集]ボタンがクリックされたとき、いかにして各カラムがテキスト・ボックスに変更され、[更新]、[キャンセル]ボタンに置き換わったのか、その仕組みを解説していこう。
そのためにはまず、各カラムには「通常モード用テンプレート」と「編集モード用テンプレート」を定義できることを解説しなければならない。ここまでの解説では、asp:TemplateColumnコントロールにItemTemplateだけを指定して、テンプレートの定義を行っていたが、このほかにも以下のようにEditItemTemplateを並記して、編集モード時に出力されるテンプレートを定義することが可能だ。編集モード用テンプレートは、データソース要素を編集可能な状態で出力するために、一般的にasp:TextBoxやasp:DropDownListコントロールなどサーバ・コントロールを用いて定義される。
<asp:TemplateColumn>
<ItemTemplate>
<通常モードで出力されるテンプレート>
</ItemTemplate>
<EditItemTemplate>
<編集モードで出力されるテンプレート>
</EditItemTemplate>
</asp:TemplateColumn>
例えば、日付を表示するカラムを編集モード時にはカレンダー・コントロール(未解説)で表示したければ、次のようにテンプレートを定義すればよい。
<asp:TemplateColumn HeaderText="更新日時">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "date") %>
</ItemTemplate>
<EditItemTemplate>
<asp:Calendar id="cal"
SelectedDate='<%# DataBinder.Eval(Container.DataItem, "date") %>' runat="server" />
</EditItemTemplate>
</asp:TemplateColumn>
一方、asp:BoundColumnカラム・コントロールで定義されたテンプレートでは、編集モード用テンプレートを定義することはできず、編集モード時には無条件でasp:TextBoxコントロールが表示される。このため、もし通常モード時にはasp:BoundColumnコントロールが利用できたとしても、編集モード時にはasp:TextBoxコントロール以外を使いたければ、そのカラムをasp:TemplateColumnコントロールで書き直す必要がある。
それ以外のasp:ButtonColumnコントロールとasp:HyperLinkColumnは、編集モード時になっても特に変化はない。つまり、EditItemTemplateが定義されていないasp:TemplateColumnのように振る舞う。
■asp:EditCommandColumnコントロール
編集モードを利用するには、編集用テンプレートに加えて、編集モードに移行するためのコマンド・ボタンが必要である。これは例えば以下のように定義する。すでに表12.1に示したように、コマンド名がedit、update、cancelのコマンド・ボタンは、こうした用途にあらかじめ予約されており、asp:DataGridコントロールの属性で個別にイベント・ハンドラを指定することができる。
<asp:TemplateColumn>
<ItemTemplate>
<asp:LinkButton Text="編集" CommandName="edit" runat="server" />
</ItemTemplate>
<EditItemTemplate>
<asp:LinkButton Text="更新"
CommandName="update" runat='server" />
<asp:LinkButton Text="キャンセル"
CommandName="cancel" runat="server" />
</EditItemTemplate>
</asp:TemplateColumn>
もっとも、asp:DataGridコントロールにこのようなカラムを定義する必要はなく、上記のasp:TempalteColumnは、特別に用意されたasp:EditCommandColumnコントロールを以下のように使用して代用することができる。asp:EditCommandColumnカラム・コントロール1つで、上記と同じくedit、update、cancel、3つのコマンド・ボタンがモードに応じて適切に表示されるカラムを定義することができる。
<asp:EditCommandColumn ButtonType="LinkButton"
EditText="編集" UpdateText="更新" CancelText="キャンセル" />
■編集モード用イベント・ハンドラ
編集モード用テンプレートとコマンド・ボタンを用意したら、あとはイベント・ハンドラの実装を行う。
まず表12.1に示したコマンド名とイベント・ハンドラ設定属性の対応に従って、asp:DataGridコントロールに属性を追加し、次のようにイベント・ハンドラ名(ここではdatagrid1_XXX)を指定する。イベント・ハンドラ名は任意に指定することができる。
<asp:DataGrid id="datagrid1"
AutoGenerateColumns="false"
OnCancelCommand="datagrid1_Cancel"
OnEditCommand="datagrid1_Edit"
OnUpdateCommand="datagrid1_Update"
runat="server">
次に各イベント・ハンドラを次のように実装する。
void datagrid1_Edit(object sender, DataGridCommandEventArgs e) {
datagrid1.EditItemIndex = e.Item.ItemIndex;
datagrid1.DataSource = CreateDataSource();
datagrid1.DataBind();
}
void datagrid1_Cancel(object sender, DataGridCommandEventArgs e) {
datagrid1.EditItemIndex = -1;
datagrid1.DataSource = CreateDataSource();
datagrid1.DataBind();
}
editコマンドのイベント・ハンドラ(datagrid1_Edit)では、一般的に編集モードへの切り替えを、cancelコマンドのイベント・ハンドラ(datagrid1_Cancel)では、一般的に通常モードへの切り替えを実装する。asp:DataGridコントロールのモードは、DataGrid.EditItemIndexプロパティの値で制御することができる。このプロパティにデータソースのインデックスを指定すれば、そのインデックスで示された要素行が編集モードに切り替えられる。ここで指定するインデックスは、イベント・パラメータとして渡されたe.Item(DataGridItemオブジェクト)のItemIndexプロパティで知ることができる。なお、複数行をまとめて編集モードに切り替えることはできない。
また、EditItemIndexプロパティに-1を代入すれば、通常モードに切り替えられる。
残るupdateコマンドのイベント・ハンドラ(datagrid1_Update)では、cancelイベント・ハンドラと同じく通常モードへの切り替えを行うが、通常はその前に編集モードで入力された情報を受け取り、これでデータソースの更新を行う必要があるはずだ。
void datagrid1_Update(object sender, DataGridCommandEventArgs e) {
int fileid = (int) datagrid1.DataKeys[e.Item.ItemIndex];
TextBox filename = (TextBox) e.Item.Cells[0].Controls[0];
TextBox filesize = (TextBox) e.Item.Cells[1].Controls[0];
TextBox date = (TextBox) e.Item.Cells[2].Controls[0];
/* ユーザーが入力した情報を元にデータソースの更新を行う */
/* 通常モードへ切り替え */
datagrid1.EditItemIndex = -1;
datagrid1.DataSource = CreateDataSource();
datagrid1.DataBind();
}
まずカラムに設定されたテキスト・ボックスなどのコントロールから情報を取得する方法だが、これにはまずコントロールへの参照を取得する必要がある。これは、e.Itemプロパティに格納されたDataGridItemオブジェクトから、次のコレクションへアクセスすることで入手できる。
Cells[<カラム>].Controls[<コントロールのインデックス>]
asp:DataListコントロールでは同様の処理にControl.FindControlメソッドを利用したが、このメソッドを利用するにはコントロールにID値が明示的に設定されている必要がある。しかし、このサンプルのようにasp:BoundColumnコントロールで自動的に生成されたテキスト・ボックスにはID値を設定できないため(内部的には設定されている)、この手法は使えない。もしasp:TemplateColumnのEditItemTemplateでID値を指定して定義したコントロールであれば、FindControlメソッドを利用できるが、このサンプル・プログラムではすべてasp:BoundColumnコントロールの自動生成に頼っている。そこで、上記のようにCellsコレクションからたどって、目的のコントロールへアクセスするしかないわけだ。
このCellsコレクションは、複数のTableCellオブジェクトを要素として格納するTableCellCollectionオブジェクトである。そして、Cellsコレクションの各要素はそれぞれ各カラムに対応して、情報を格納している(図12.3)。そこでカラムを指定したら、今度はTableCellオブジェクトのControlsコレクションへアクセスして、目的のコントロールを取得する。<コントロールのインデックス>は通常0でよいが、1つのカラムに複数のコントロールが配置されているときには、適切なインデックスを指定する。
以上の作業で目的のコントロールを取得できるが、<カラム>や<コントロールのインデックス>に指定する値は、直感的に分かりづらいだろう。そこで、この値を調べるときには、次のように@PAGEディレクティブに「Trace="true"」を追加するとよい。
<%@ PAGE LANGUAGE="C#" Trace="true" %>
すると、次の図12.4に示すように、asp:DataGridコントロールによって出力されたテーブルに、どのようなコントロールがどのような順番で格納されているのか調べることができる。
以上の作業でコントロールから情報を入手することができる。しかしそれだけではなく、データソースには格納されていながら、データ連結はされていない情報が更新時に必要になる場合もあるはずだ。例えば、データベースの更新を行うには、通常アイテムのID値が必要だが、これはユーザーに見せる必要はないので、データ連結は行われない。このためテーブルに出力されず、結果としてCellsコレクションをたどって取得することはできない。こうしたデータソースの情報へイベント・ハンドラからアクセスするには、データキーを設定すればよい。データキーを利用するには、まず次のようにasp:DataGridコントロールのDataKeyField属性に、アクセスしたいデータソースのフィールドを指定する。
<asp:DataGrid id="datagrid1"
AutoGenerateColumns="false"
DataKeyField="fileid" データキー・フィールドをfileidに設定
runat="server">
するとイベント・ハンドラでは、次のようにDataGrid.DataKeysプロパティにインデックスを指定することで、データキー・フィールドへアクセスすることができる。
int fileid = (int) datagrid1.DataKeys[e.Item.ItemIndex];
Copyright© Digital Advantage Corp. All Rights Reserved.