DataGridにレコードの編集機能を付加するには
● DataGridにレコードの編集機能を付加するサンプル(手動方式)

図 DataGridにレコードの編集機能を付加するサンプル(手動方式)
このサンプルは、DataGridにレコードの編集機能を追加しています。DataGridから編集ボタンをクリックすると得意先名、担当者名、電話番号がTextBoxに表示されますので編集後、確定ボタンをクリックします。中止ボタンをクリックすると編集がキャンセルされます。このサンプルでは、確定ボタンをクリックしたときデータベースへの書き込みをSQLコマンドで行っています。
このサンプルでは、以下のノウハウを習得することができます。
▲ DataGridにレコードの編集機能を付加する方法
▲ DataGridのBoundColumnの使い方
▲ DataGridのEditCommandColumnの使い方
▲ DataGridのOnEditCommandイベントの使い方
▲ DataGridのOnUpdateCommand, OnCancelCommandイベントの使い方
▲ DataGridから編集したレコードをSQLコマンドでデータベースに反映する方法
サンプルの行85-125では、DataGridを定義しています。行88では、OnEditCommandイベントを登録しています。このイベントは、DataGridから編集ボタンをクリックすると発生します。行89では、OnUpdateCommandイベントを登録しています。このイベントは、DataGridから確定(更新)ボタンをクリックすると発生します。確定ボタンは、レコードが編集モードのときに表示されます。行90では、OnCancelCommandイベントを登録しています。このイベントは、DataGridから中止ボタンをクリックすると発生します。中止ボタンは、レコードが編集モードのときに表示されます。行100では、EditItemStyleで編集行のスタイルを設定しています。CssClassプロパティには、CSSのクラス名を設定しています。CSSのクラスdgrdEditItemStyleは、外部ファイルstyle1.cssに登録されています。行101-124の<Columns>…</Columns>では、BoundColumn, EditCommandColumnを定義しています。行102-106では、BoundColumnで得意先IDを表示しています。DataFieldプロパティには、得意先IDのフィールド名(CustomerID)を設定しています。HeaderTextプロパティには、「ID」を設定しています。ReadOnlyプロパティにTrueを設定して読み込み専用にしています。この場合、編集ボタンをクリックしても得意先IDを編集することができません。行107-109では、BoundColumnで得意先名を表示しています。DataFieldプロパティには、得意先名のフィールド名(CompanyName)を設定しています。HeaderTextプロパティには、「得意先名」を設定しています。このカラムでは、ReadOnlyプロパティを省略していますのでFalseが採用されます。編集ボタンをクリックしたとき、ReadOnlyプロパティにFalseが設定されているカラムが編集対象になります。以下、同様に担当者名、電話番号を表示しています。行116-123では、EditCommandColumnで編集ボタン、確定(更新)ボタン、中止ボタンを定義しています。確定ボタンと中止ボタンは、編集モードのときに表示されます。
85: <asp:DataGrid id="dgrdCustomers"
runat="server"
88:
OnEditCommand="dgrdCustomers_EditCommand"
89:
OnUpdateCommand="dgrdCustomers_UpdateCommand"
90:
OnCancelCommand="dgrdCustomers_CancelCommand"
:::: >
100: <EditItemStyle
CssClass="dgrdEditItemStyle" />
101: <Columns>
102: <asp:BoundColumn
103:
DataField="CustomerID"
104:
HeaderText="ID"
105:
ReadOnly="True"
106:
ItemStyle-HorizontalAlign="Right" />
107: <asp:BoundColumn
108:
DataField="CompanyName"
109:
HeaderText="得意先名" />
::::
116: <asp:EditCommandColumn
117:
ButtonType="PushButton"
118:
EditText="編集"
119:
UpdateText="確定"
120:
CancelText="中止">
121:
<ItemStyle BackColor="Yellow"
122:
HorizontalAlign="Center" />
123:
</asp:EditCommandColumn>
124:
</Columns>
125: </asp:DataGrid>
Page_Load()イベントの行13-14では、OleDbConnection, OleDbDataAdapterのインスタンスを生成してモジュールレベルの変数に保存しています。行15では、Cacheのキーを生成して変数に保存しています。行17-18は、ページが最初にロードされたときに実行されます。Sub LoadData()では、得意先テーブルのレコードを抽出してDataTableを生成します。DataTableは、ポストバックされてときに再使用できるようにメモリ上にキャッシュします。Sub BindDataGrid()では、DataTableをDataGridにバインドして表示します。行20-23は、ページがポストバックされたときに実行されます。行20では、メモリ上にキャッシュされたDataTableを変数に保存しています。キャッシュが無効になっているときは、Sub LoadData()を呼び出してDataTableを生成します。
11: Sub Page_Load()
12: Dim strSQL As String = "Select
top 10 * From Customers Order by CustomerID"
13: mcon = New
OleDbConnection(ConfigurationSettings.AppSettings("conStringNw"))
14: mda = New OleDbDataAdapter(strSQL,
mcon)
15: mstrCacheKey =
"Customers" & Request.Path
16: If Not IsPostBack Then
17: LoadData()
18: BindDataGrid()
19: Else
20: mdt =
Cache(mstrCacheKey)
21: If mdt Is Nothing Then
22: LoadData()
23: End If
24: End If
25: End Sub
Sub LoadData()では、OleDbDataAdapterのFill()メソッドで得意先テーブルからレコードを抽出してDataSetに格納します。さらに、DataSetのTablesコレクションから得意先テーブルのDataTableを生成して変数に保存します。ここで生成したDataTableは、ポストバックされたときに再使用するためにWebサーバのメモリ上にキャッシュします。
27: Sub LoadData()
28: Dim ds As New DataSet()
29: mda.Fill(ds,"Customers")
30: mdt =
ds.Tables("Customers")
31: Cache(mstrCacheKey) = mdt
32: End Sub
Sub BindDataGrid()では、DataGridのDataSourceプロパティに得意先テーブルのDataTableを設定して、DataBind()メソッドでバインドします。これで、得意先テーブルがDataGrid上に表示されます。
34: Sub BindDataGrid()
35: With dgrdCustomers
36:
.DataKeyField="CustomerID"
37: .DataSource = mdt
38: .DataBind()
39: End With
40: End Sub
DataGridから編集ボタンをクリックすると、OnEditCommandイベントが発生します。このイベントでは、DataGridCommandEventArgsのItem.ItemIndexプロパティをDataGridのEditItemIndexプロパティに設定して編集モードに切り替えています。Item.ItemIndexには、編集ボタンをクリックした行のインデックス番号が格納されています。DataGridのEditItemIndexプロパティにインデックス番号を設定すると、その行が編集モードになります。Sub BindDataGrid()を呼び出して、DataGridをバインドすると編集ボタンをクリックした行が編集モードで表示されます。編集モードに切り替わると、得意先名、担当者名、電話番号がTextBox上に表示されて編集可能になります。編集ボタンは、確定ボタンと中止ボタンに切り替わります。
42: Sub dgrdCustomers_EditCommand(s As Object, e As
DataGridCommandEventArgs)
43: dgrdCustomers.EditItemIndex =
e.Item.ItemIndex
44: BindDataGrid()
45: End Sub
レコードを編集して確定(更新)ボタンをクリックすると、OnUpdateCommandイベントが発生します。このイベントでは、編集したレコードをデータベースに反映します。行48-49では、得意先テーブルのレコードを更新するSQLを生成しています。レコードを更新するには、SQLのUpdateステートメントを使用します。行50では、OleDbCommandのインスタンスを生成しています。引数には、SQLコマンドとOleDbConnectionを指定します。行52では、DataGridのDataKeysプロパティから主キー(CustomerID)を取得して変数に保存しています。DataKeysの引数には、アイテムのインデックス番号を指定します。行53-55では、DataGridCommandEventArgsクラスのItem.Cells().Controls()プロパティから得意先名、担当者名、電話番号のTextBoxを取得してTextプロパティの値を変数に保存しています。つまり、編集データを取得して格納しています。行57-62のWith…End Withでは、SQLのパラメータ変数に編集した値を設定しています。行64-66では、OleDbConnectionのOpen()メソッドでデータベースを開いて、OleDbCommandのExecuteNonQuery()メソッドでSQLのUpdateステートメントを実行して、データベースを閉じています。これで、DataGridから編集したレコードがデータベースに反映されます。行68-70では、編集モードを解除して得意先テーブルから抽出したレコードを再表示しています。DataGridのEditItemIndexプロパティに-1を設定すると編集モードが解除されます。
47: Sub dgrdCustomers_UpdateCommand(s As Object, e
As DataGridCommandEventArgs)
48: Dim strSqlUpdate As String =
"Update Customers Set CompanyName=@CompanyName," & _
49:
"ContactName=@ContactName, Phone=@Phone Where
CustomerID=@CustomerID"
50: Dim cmd As New
OleDbCommand(strSqlUpdate,mcon)
52: Dim intCustomerID As Integer =
dgrdCustomers.DataKeys(e.Item.ItemIndex)
53: Dim strCompanyName As String =
CType(e.Item.Cells(1).Controls(0), TextBox).Text
54: Dim strContactName As String =
CType(e.Item.Cells(2).Controls(0), TextBox).Text
55: Dim strPhone As String = CType(e.Item.Cells(3).Controls(0),
TextBox).Text
57: With cmd.Parameters
58:
.Add("@CompanyName",strCompanyName)
59:
.Add("@ContactName",strContactName)
60:
.Add("@Phone",strPhone)
61:
.Add("@CustomerID",intCustomerID)
62: End With
63:
64: mcon.Open()
65: cmd.ExecuteNonQuery()
66: mcon.Close()
67:
68: dgrdCustomers.EditItemIndex = -1
69: LoadData()
70: BindDataGrid()
71: End Sub
DataGridから中止ボタンをクリックしたときは、OnCancelCommandイベントが発生します。このイベントでは、編集モードを解除して編集前のデータを再表示します。DataGridのEditItemIndexプロパティに-1を設定すると編集モードが解除されます。Sub BindDataGridでは、DataGridにDataTableをバインドします。
73: Sub dgrdCustomers_CancelCommand(s As Object, e
As DataGridCommandEventArgs)
74: dgrdCustomers.EditItemIndex = -1
75: BindDataGrid()
76: End Sub
このサンプルでは、編集したレコードをデータベースに反映するのに以下のようなUpdateステートメントを使用しています。Where句では、CustomerID=@CustomerIDを指定していますので得意先IDでレコードを検索して更新します。得意先IDでレコードを検索して更新する場合、他のクライアントと競合するとレコードが2重更新される可能性があります。レコードの2重更新を防止する方法については、後述するTipsを参照してください。
Update Customers
Set CompanyName=@CompanyName ContactName=@ContactName,
Phone=@Phone
Where CustomerID=@CustomerID
Tip
|
レコードの2重更新を防止するには たとえば、得意先テーブルの2重更新を防止するには新規フィールドConcurrencyIDを追加してレコードの更新回数を管理します。DataGirdから確定ボタンをクリックしてレコードを更新するときは、以下のようなSQLを使用します。 Update Customers Set CompanyName=@CompanyName ContactName=@ContactName,
Phone=@Phone
ConcurrencyID = ConcurrencyID + 1 Where CustomerID=@CustomerID And ConcurrencyID=@ConcurrencyID @CustomerIDには、更新するレコードの得意先IDを設定します。@ConcurrencyIDには、更新前に取得したConcurrencyIDを設定します。他のクライアントからすでに更新されているときは、ConcurrencyIDが不一致になりますので更新されません。レコードが更新されたか調べるには、OleDbCommandのExecuteNonQuery()メソッドの戻り値を調べます。戻り値が0のときは、更新されていませんのでDataGridに最新のデータを表示して再試行させます。 更新前のConcurrenyIDは、DataGridのBoundColumnで埋め込んでおきます。Visibleプロパティには、Falseを設定して非表示にします。 <asp:BoundColumn DataField=”ConcurrencyID” Visible=”False” /> |
● DataGridにレコードの編集機能を付加するサンプル(自動方式)

図 DataGridにレコードの編集機能を付加するサンプル(自動方式)
このサンプルは、DataGridにレコードの編集機能を追加しています。機能的には、すでに解説した「手動方式」と同じです。このサンプルでは、DataGridのレコードを編集して確定ボタンをクリックしたとき、OleDbDataAdapterのUpdate()メソッドを使用してデータベースに反映しています。つまり、データベースの更新処理を自動化しています。
このサンプルでは、以下のノウハウを習得することができます。
▲ OleDbCommandBuilderの使い方
▲ OleDbDataAdapterのUpdate()メソッドの使い方
サンプルの行81-121では、DataGridを定義しています。行97-120では、<Columns>…</Columns>内にBoundColumn, EditCommandColumnで得意先ID、得意先名、担当者名、電話番号、編集ボタンを表示しています。
81: <asp:DataGrid id="dgrdCustomers"
runat="server"
90: ::: >
97: <Columns>
::::
120:
</Columns>
121: </asp:DataGrid>
Sub Page_Load()の行13では、得意先テーブルからレコードを抽出するSQLを生成しています。行14-15では、OleDbConnection, OleDbDataAdapterのインスタンスを生成しています。OleDbConnectionの引数には、Web.configに登録されているデータベース接続情報を指定しています。OleDbDataAdapterの引数には、SQLとOleDbConnectionを指定しています。行16では、OleDbCommandBuilderのインスタンスを生成しています。引数には、OleDbDataAdapterを指定します。OleDbCommandBuilderは、SQLのInsert, Update, Deleteステートメントを生成します。SQLコマンドは、行13のSelectステートメントをベースに生成します。このサンプルでは、以下のようなSQLが生成されます。
Insert用のSQL:
INSERT INTO Customers( CompanyKana , CompanyName , ContactName ,
ContactTitle , PostalCode , KenKana , Ken , Address1 , Address2 , Phone , Fax ,
ConcurrencyID ) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
UpdateのSQL:
UPDATE Customers SET CompanyKana = ? , CompanyName = ? ,
ContactName = ? , ContactTitle = ? , PostalCode = ? , KenKana = ? , Ken = ? ,
Address1 = ? , Address2 = ? , Phone = ? , Fax = ? , ConcurrencyID = ?
WHERE ( (CustomerID = ?)
AND ((? IS NULL AND CompanyKana IS NULL) OR (CompanyKana = ?))
AND ((? IS NULL AND CompanyName IS NULL) OR (CompanyName = ?))
AND ((? IS NULL AND ContactName IS NULL) OR (ContactName = ?))
AND ((? IS NULL AND ContactTitle IS NULL) OR (ContactTitle = ?))
AND ((? IS NULL AND PostalCode IS NULL) OR (PostalCode = ?))
AND ((? IS NULL AND KenKana IS NULL) OR (KenKana = ?))
AND ((? IS NULL AND Ken IS NULL) OR (Ken = ?))
AND ((? IS NULL AND Address1 IS NULL) OR (Address1 = ?))
AND ((? IS NULL AND Address2 IS NULL) OR (Address2 = ?))
AND ((? IS NULL AND Phone IS NULL) OR (Phone = ?))
AND ((? IS NULL AND Fax IS NULL) OR (Fax = ?))
AND ((? IS NULL AND ConcurrencyID IS NULL) OR (ConcurrencyID = ?)) )
Delete用のSQL:
DELETE FROM Customers
WHERE ( (CustomerID = ?)
AND ((? IS NULL AND CompanyKana IS NULL) OR (CompanyKana = ?))
::::
AND ((? IS NULL AND ConcurrencyID IS NULL) OR (ConcurrencyID = ?)) )
OleDbDataAdapterのUpdate()メソッドは、ここで生成されたSQLを使用してレコードを追加、更新、削除します。Update/DeleteステートメントのWhere句では、各フィールドの値が更新前の内容と一致するときのみレコードを更新/削除するようにしています。これによりレコードの2重更新が防止されます。ConcurrencyIDを使用するときは、以下のようなSQLを使用することもできます。
UPDATE Customers SET CompanyKana = ? , CompanyName = ? ,
ContactName = ? , ContactTitle = ? , PostalCode = ? , KenKana = ? , Ken = ? ,
Address1 = ? , Address2 = ? , Phone = ? , Fax = ? , ConcurrencyID = ConcurencyID+1
WHERE ( (CustomerID = ?) AND (ConcurrencyID = ?))
行19-20は、ページが最初にロードされたときに実行されます。Sub LoadData()では、得意先テーブルのレコードを抽出してDataTableを生成します。Sub BindDataGrid()では、DataTableをDataGridにバインドして表示します。行22-25は、ページがポストバックされたときに実行されます。行22では、キャッシュされたDataTableを変数に保存しています。キャッシュが無効になっているときは、Sub LoadData()を呼び出してDataTableを生成します。
12: Sub Page_Load()
13: Dim strSQL As String =
"Select top 10 * From Customers Order by CustomerID"
14: mcon = New
OleDbConnection(ConfigurationSettings.AppSettings("conStringNw"))
15: mda = New OleDbDataAdapter(strSQL,
mcon)
16: mcb = New OleDbCommandBuilder(mda)
17: mstrCacheKey =
"Customers" & Request.Path
18: If Not IsPostBack Then
19: LoadData()
20: BindDataGrid()
21: Else
22: mdt =
Cache(mstrCacheKey)
23: If mdt Is Nothing Then
24: LoadData()
25: End If
26: End If
27: End Sub
DataGridから確定(更新)ボタンをクリックすると、OnUpdateCommandイベントが発生します。このイベントでは、編集したレコードをデータベースに反映しています。行51では、DataGridのDataKeysプロパティから得意先IDを取得して変数に保存しています。行52-54では、DataGridから得意先名、担当者名、電話番号の編集データを取得して変数に保存しています。行55では、DataTableのRo
50: Sub dgrdCustomers_UpdateCommand(s As Object, e
As DataGridCommandEventArgs)
51: Dim intCustomerID As Integer =
dgrdCustomers.DataKeys(e.Item.ItemIndex)
52: Dim strCompanyName As String =
CType(e.Item.Cells(1).Controls(0), TextBox).Text
53: Dim strContactName As String =
CType(e.Item.Cells(2).Controls(0), TextBox).Text
54: Dim strPhone As String =
CType(e.Item.Cells(3).Controls(0), TextBox).Text
55: Dim dr As DataRow = mdt.Ro
56: If Not (dr Is Nothing) Then
57:
dr("CompanyName") = strCompanyName
58:
dr("ContactName") = strContactName
59: dr("Phone")
= strPhone
60: End If
64: mda.Update(mdt)
65: dgrdCustomers.EditItemIndex = -1
66: BindDataGrid()
67: End Sub
このサンプルでは、OleDbCommandBuilderで生成されたSQLを使用していますが、ストアドプロシージャを使用してデータベースに反映することもできます。この場合、OleDbDataAdapterのInsertCommand, UpdateCommand, DeleteCommandプロパティにストアドプロシージャを設定してからUpdate()メソッドを実行します。