ASP.NET 徹底活用術のホームへ戻る

DataGridに編集機能を組み込む ~dg1.aspx

 

DataGridに編集機能を組み込むには[編集]ボタン列を追加して、DataGridEditCommandUpdateCommandCancelCommandイベントハンドラを登録します。これらのイベントハンドラには、DataSet(DataTable)に格納されているレコードを更新したり、データベースに書き込む処理を追加します。ここで作成するサンプルは、DataGridから編集したデータをDataTable上で更新して、DataAdapterUpdateメソッドでデータベースに反映します。

 

新規Webフォームを作成したら、ツールボックスから[Label]をドラッグしてデザイナにドロップします。デザイナにLabel1のオブジェクトが作成されたら、プロパティウィンドウから(ID)プロパティを「lblMessage」に書き換えます。さらに、EnableViewStateプロパティに「False」、ForeColorプロパティに「Red」を設定します。

 

ツールボックスから[DataGrid]をドラッグ&ドロップします。デザイナにDataGrid1のオブジェクトが作成されたら、DataGrid1の右クリックから[プロパティビルダ]を選択します。「DataGrid1プロパティビルダ」が表示されたら、左側から[]をクリックします。「実行時に自動的に列を作成する」のチェックを外したら、「使用可能な列」から[連結列]を選択して[>]ボタンをクリックします。「選択された列」に「連結列」が移動したら、BoundColumnプロパティの「ヘッダーテキスト」に「ID」、「データフィールド」に「CustomerID」を入力します。「読み取り専用」をクリックしてチェックマークを付けます(CustomerIDは、主キーのフィールドのため変更不可とします)。同様の手順で、表1の「得意先」、「担当」、「電話」の連結列を追加します。

 

1 DataGridに追加する連結列とプロパティ

ヘッダーテキスト

データフィールド

読み取り専用

ID

CustomerID

X

得意先

CompanyName

 

担当

ContactName

 

電話

Phone

 

 

 

「使用可能な列」から[ボタン列]を展開したら、[編集、更新、キャンセル]を選択して[>]ボタンをクリックします。EditCommandColumnプロパティの「ボタンの種類」から[PushButton]を選択したら、[OK]ボタンをクリックしてプロパティビルダを閉じます。DataGridのヘッダーに連結列と編集ボタン列が表示されます。

 

1: DataGridのプロパティウィンドウから[編集]ボタン列を追加

 

 

2: DataGridのヘッダーに連結列と編集ボタン列が表示された

 

 

デザイナの右クリックから[コードの表示]を選択します。コードウィンドウが表示されたら、Page_Loadイベントハンドラに、次のコードを追加します。

 

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _

  Handles MyBase.Load

  Dim strSQL As String = "SELECT CustomerID, CompanyName, " & _

    "ContactName, Phone FROM Customers " & _

    "WHERE CustomerID > 45 " & _

    "ORDER BY CustomerID"

  Dim strConnectionString As String = _

    String.Format("PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA Source={0}", _

    Server.MapPath("~/webdb/Nwind.mdb"))

  mcon = New OleDbConnection(strConnectionString)

  mda = New OleDbDataAdapter(strSQL, mcon)

  mcb = New OleDbCommandBuilder(mda)

 If Not IsPostBack Then

    BindDataGrid()

  Else

    mdt = CType(Session(conSessionKey), DataTable)

  End If

End Sub

 

Page_Loadイベントハンドラでは、ConnectionDataAdapterCommandBuilderのインスタンスを生成します。CommandBuilderは、DataAdapterSelecteCommandプロパティに格納されているSQLを基に、レコードを追加、更新、削除するためのSQLを自動的に作成します。

 

DataAdapterSelectedCommandに格納されているSQLステートメント:

SELECT CustomerID, CompanyName, ContactName, Phone

FROM Customers

WHERE CustomerID > 45

ORDER BY CustomerID

 

CommandBuilderが作成したUPDATEステートメントのWHERE句に余分なフィールドが付加されていますが、これはレコードが他のユーザーから更新されていないか調べるためです。DataAdapterUpdateメソッドは、更新対象となるレコードのフィールド値(SELECTステートメントで指定したすべてのフィールド値)が変更されていないことをチェックすることにより「楽観的ロック」を実現しています。

 

CommandBuilderが生成した更新用のSQLステートメント:

UPDATE Customers SET CompanyName = ? ,

  ContactName = ? , Phone = ?

  WHERE ( (CustomerID = ?)

  AND ((? = 1 AND CompanyName IS NULL) OR (CompanyName = ?))

  AND ((? = 1 AND ContactName IS NULL) OR (ContactName = ?))

  AND ((? = 1 AND Phone IS NULL) OR (Phone = ?)) )

 

BindDataGridメソッドは、CreateDataTableメソッドを実行してDataTableに得意先テーブル(Customers)のレコードを格納します。次に、DataGridDataSourceプロパティにDataTableのオブジェクトを設定して、DataBindメソッドを実行します。DataGridDataKeyFieldプロパティには、得意先テーブルの主キー(CustomerID)を設定しておきます。ここで設定した主キーは、後述するUpdateCommandイベントハンドラで使用します。

 

Sub BindDataGrid()

  CreateDataTable()

  With DataGrid1

    .DataKeyField = "CustomerID"

    .DataSource = mdt

    .DataBind()

  End With

End Sub

 

CreateDataTableメソッドは、オブジェクト変数に格納されているDataTableが無効か調べます。DataTableオブジェクトが無効なら、DataAdapterFillメソッドを実行して得意先テーブルをDataTableに取り込みます。DataTableはポストバックされたときに再利用するために、Session変数に格納しておきます。

 

Sub CreateDataTable()

  If mdt Is Nothing Then

    mdt = New DataTable

    mda.Fill(mdt)

    mdt.PrimaryKey = New DataColumn() {mdt.Columns("CustomerID")}

    Session(conSessionKey) = mdt

  End If

End Sub

 

コードウィンドウの「クラス名」のドロップダウンリストから[DataGrid1]、「メソッド名」のドロップダウンリストから[EditCommand]を選択して、EditCommandイベントハンドラを作成します。同様の手順で、UpdateCommandCancelCommandイベントハンドラを作成します。

 

EditCommandイベントハンドラは、DataGridから[編集]ボタンをクリックしたときに実行されます。このイベントハンドラでは、カレント行を編集行に切り替えてDataGridを再表示します。カレント行を編集行に切り替えるには、DataGridEditItemIndexプロパティにカレント行のインデックス番号を設定します。カレントの行番号は、e.Item.ItemIndexプロパティに格納されています。BindDataGridメソッドを実行すると、DataGridが再表示されてカレント行が編集行として表示されます。このとき、[編集]ボタンが[更新][キャンセル]ボタンに切り替わります。

 

Private Sub DataGrid1_EditCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _

  Handles DataGrid1.EditCommand

  DataGrid1.EditItemIndex = e.Item.ItemIndex

  BindDataGrid()

End Sub

 

UpdateCommandイベントハンドラは、DataGridから[更新]ボタンをクリックしたときに実行されます。このイベントハンドラでは、編集行から編集済みのデータを取得してDataTable上のレコードを更新します。DataGridの編集行からデータを取得するには、e.Item.Cellsコレクションを使用します。Cellに格納されているTextBoxのオブジェクトを取得するには、Controlsコレクションを使用します。たとえば、編集行から「得意先」のTextBoxのオブジェクトを取得するには、e.Item.Cells(1).Controls(0)のように記述します。

 

編集したレコードの主キー(CustomerID)は、DataGridDataKeysコレクションに格納されています。DataTableに格納されているレコードを更新してデータベースに反映するには、UpdateRecordメソッドを実行します。編集行を通常行として表示するには、DataGridEditItemIndexプロパティに「-1」を設定してBindDataGridメソッドを実行します。

 

Private Sub DataGrid1_UpdateCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _

  Handles DataGrid1.UpdateCommand

  Dim strCompanyName As String = CType(e.Item.Cells(1).Controls(0), TextBox).Text

  Dim strContactName As String = CType(e.Item.Cells(2).Controls(0), TextBox).Text

  Dim strPhone As String = CType(e.Item.Cells(3).Controls(0), TextBox).Text

  Dim intCustomerID As Integer = DataGrid1.DataKeys(e.Item.ItemIndex)

  UpdateRecord(strCompanyName, strContactName, strPhone, intCustomerID)

  DataGrid1.EditItemIndex = -1

  BindDataGrid()

End Sub

 

CancelCommandイベントハンドラは、DataGridから[キャンセル]ボタンをクリックしたときに実行されます。このイベントハンドラでは、DataGridEditItemIndexプロパティに「-1」を設定して編集行を通常行として再表示します。

 

Private Sub DataGrid1_CancelCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _

  Handles DataGrid1.CancelCommand

  DataGrid1.EditItemIndex = -1

  BindDataGrid()

End Sub

 

UpdateRecordメソッドは、DataTableに格納されているレコードを検索して目的のレコードを見つけたらフィールド値を更新します。次に、DataAdapterUpdateメソッドを実行してDataTableの内容をデータベースに反映します。Updateメソッドは、CommandBuilderが自動生成したSQLを使用して、レコードを追加、更新、削除します。たとえば、DataTableのレコードが変更されているときは、SQLUPDATEステートメントを使用してデータベースのレコードを更新します。レコードが他のユーザーからすでに変更されているときは、同時実行の例外エラー(DBConcurrencyException)が発生します。ここでは、Try…Catch…Finallyステートメントで例外エラーを拾って、エラーメッセージを表示します。

 

同時実行の例外エラーが発生したときは、DataGridに最新のデータを表示するためにDataTableオブジェクトを無効にします。

 

リスト1: DataTableを更新してデータベースに反映させる

Private Function UpdateRecord(ByVal strCompanyName As String, _

  ByVal strContactName As String, _

  ByVal strPhone As String, _

  ByVal intCustomerID As Integer) As Integer

  Dim intRetValue As Integer = 0

  Dim dr As DataRow = mdt.Rows.Find(intCustomerID)

  If Not (dr Is Nothing) Then

    dr("CompanyName") = strCompanyName

    dr("ContactName") = strContactName

    dr("Phone") = strPhone

    Try

      mda.Update(mdt)

      intRetValue = 1

    Catch ex As DBConcurrencyException

      lblMessage.Text = "他のユーザーから変更されています! 再試行してください..."

      mdt = Nothing

    Catch ex As Exception

      lblMessage.Text = ex.Message.ToString

    Finally

      Session(conSessionKey) = mdt

    End Try

  End If

  Return intRetValue

End Function

 

3: DataGridから[編集]ボタンをクリックしたところ

 

 

Tip DataGridの編集行以外のボタンをクリックしたとき無効とするには

 

DataGridから[編集]ボタンをクリックしてカレント行が編集行として表示されているときは、レコードを編集して[更新]ボタンをクリックするか、[キャンセル]ボタンをクリックします。ところが、誤って別の行の[編集]ボタンをクリックするとレコードの編集が無効になります。この不都合を回避するために、編集行以外のボタンをクリックしたときエラーメッセージを表示して無効にします。

 

DataGridから[編集][削除]などのボタンをクリックすると、WebページがポストバックされてItemCommandイベントハンドラが実行されます。このイベントハンドラでは、CheckIsEditingメソッドを実行してDataGridが編集中か調べます。CheckIsEditingの引数には、e.CommandNameプロパティを指定します。CommandNameプロパティには、「Edit」、「Update」、「Cancel」などのコマンド名が格納されています。

 

Private Sub DataGrid1_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _

  Handles DataGrid1.ItemCommand

  CheckIsEditing(e.CommandName)

End Sub

 

CheckIsEditingメソッドは、DataGridEditItemIndexプロパティをチェックしてDataGridが編集中か調べます。編集中のときは、エラーメッセージを表示します。

 

Sub CheckIsEditing(ByVal strCommandName As String)

  If DataGrid1.EditItemIndex <> -1 Then

    If strCommandName <> "Cancel" AndAlso strCommandName <> "Update" Then

      lblMessage.Text = "編集中のデータがまだ保存されていません!"

      IsEditing = True

    End If

  End If

End Sub

 

 

ASP.NET 徹底活用術のホームへ戻る