DataGridにレコードを更新する機能を追加したサンプル

 

DataGridにレコードを更新する機能を追加したサンプル

 

このサンプルは、親ウィンドウのDataGridからレコードを選択して子ウィンドウから編集することができます。子ウィンドウから編集したレコードは、親ウィンドウのDataGridに表示されます。

 

レコードを編集するには、親ウィンドウに表示されているDataGridからレコードを選択します。次に、親ウィンドウからレコードの更新ボタンをクリックします。子ウィンドウのテキストボックスに選択したレコードが表示されたら、編集して子ウィンドウから[更新]ボタンをクリックします。子ウィンドウから更新ボタンをクリックしても、子ウィンドウは開いた状態になっていますので再更新することができます。[閉じる]のボタンをクリックすると、子ウィンドウを閉じます。

 

更新されたレコードは、親ウィンドウのDataGridに表示されます。DataGridには、得意先テーブルのレコードが得意先IDの降順に10件表示されます。

 

 

◆プログラムDataGridUpdate.aspx/PopupUpdate.aspxのポイント

 

¶ポイント1 DataGridに表示されていないカラムを編集するには

 

Web Matrixのテンプレートを使用すれば、DataGridにレコードを編集する機能を簡単に追加することができます。ところが、DataGridに編集機能を組み込んだ場合、編集できるレコードのフィールドがDataGridに表示されているカラムに限定されるため、あまり実用的ではありません。このサンプルでは、レコードの編集処理を、子ウィンドウに分離することによりこの問題を解決しています。子ウィンドウからは、DataGridに表示されていないカラムも編集することができます。また、子ウィンドウと親ウィンドウを連動させていますので、子ウィンドウで編集したレコードが親ウィンドウのDataGridに反映されます。

 

¶ポイント2 同時実行違反の対処

 

このサンプルでは、編集したレコードをデータベースに反映するのにDataAdapterUpdate()メソッドを使用しています。Update()メソッドは、DataTableのレコードが変更されたときは、SQLUPDATEステートメントを実行してデータベースに反映します。

 

Update()メソッドは、レコードが他のクライアントからすでに変更されているとき、「同時実行違反」のエラーとします。この場合、親ウィンドウのDataGridには最新のデータを再表示しますので、子ウィンドウに表示されている内容と比較することができます。子ウィンドウに表示されているデータで上書きしても問題なければ再度、更新ボタンをクリックします。2回目の更新ボタンをクリックしたときは、DataTableが最新のデータにリフレッシュされていますので正常に更新されます。

 

 

¶ポイント3 DataGridOnItemCommandイベントで選択した行の主キーを取得するには

 

OnItemCommandイベントで選択した行の主キーを取得するには、DataGridDataKeysコレクションを参照します。DataKeysコレクションの引数には、DataGridから選択したアイテムのインデックス番号を指定します。

 

mintCustomerID = dgrdCustomers.DataKeys(e.Item.ItemIndex)

 

なお、DataKeysコレクションは、DataGridDataKeyFieldプロパティに主キーのカラム名を設定したとき有効です。

 

dgrdCustomers. DataKeyField = "CustomerID"

 

Page_Loadイベントから選択した行の主キーを取得するには、以下のように記述します。

 

mintCustomerID = dgrdCustomers.DataKeys(dgrdCustomers.SelectedIndex)

 

 

¶ポイント4 DataGridからアイテムを選択しているか調べるには

 

アイテムが選択されているか調べるには、DataGridSelectedIndexプロパティを参照します。SelectedIndex-1が格納されているときは、アイテムが選択されていません。

 

If dgrdCustomers.SelectedIndex = - 1 Then

  ・・・アイテムが選択されていないときの処理

End If

 

 

¶ポイント5 親ウィンドウから子ウィンドウに更新するレコードの得意先IDを渡すには

 

親ウィンドウから子ウィンドウに得意先IDを渡すには、QueryStringを使用します。

 

PopupUpdate.aspx?id=9999

 

子ウィンドウから得意先IDを取得するには、Request.QueryStringRequest.Paramsプロパティを使用します。

 

Request.QueryString("id")

Request.Params("id")

 

 

¶ポイント6 レコードのカラム値がNullか調べるには

 

DataRowに格納されているレコードのカラム値がNullか調べるには、DataRowIsNull()メソッドを参照します。

 

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

If dr.IsNull("Phone") Then

  ・・・電話番号がNullのときの処理

End If

 

◆メインプログラムDataGridUpdate.aspxの解説(HTML編)

 

親ウィンドウのWebフォームには、ImageButtonDataGridを作成しています。ImageButtonでは、レコードの更新ボタンを表示します。このボタンをクリックすると、子ウィンドウが開きます。DataGridには、得意先テーブルのレコードを得意先IDの降順に10件表示します。

 

151-157では、ImageButtonを定義しています。行153では、OnCommandイベントを登録しています。このイベントでは、子ウィンドウを開きます。

 

151:   <asp:ImageButton id="ibtnUpdate" runat="server"
152:     CommandName="update"
153:     OnCommand="ibtnUpdate_Command"
154:     ImageUrl="../img/update.gif"
157:      />

 

158-164では、LabelTextBoxを定義してDataGridの件数を表示しています。TextBoxReadOnlyプロパティには、Trueを設定して読み込み専用にしています。TextBoxには、ランタイム時にレコード件数を設定します。

 

158:   <asp:Label id="lblRows" runat="server"
159:     Text="
件数"
160:     />
161:   <asp:TextBox id="txtRows" runat="server"
163:     Columns="3"
164:     ReadOnly="True" />

 

177-229では、DataGridを定義しています。このDataGridでは、AutoGenerateColumnsプロパティにFalseを設定してカラムの自動生成機能を抑止しています。行191-228<Columns>…</Columns>では、TemplateColumnBoundColumnを定義しています。TemplateColumnItemTemplateでは、LinkButtonを定義しています。LinkButtonは、レコードセレクターとして使用します。BoundColumnでは、得意先テーブルの得意先ID、得意先名、担当者名、役職、電話番号、都道府県をバインドしています。

 

191:     <Columns>
192:       <asp:TemplateColumn
193:         HeaderText="<div class='dgrdHeaderBox'>1</div>">
194:         <ItemTemplate>
195:           <asp:LinkButton id="lbtnSelect" runat="server"
196:             CommandName="Select"
197:             Text="<div class='dgrdItemArrow'>4</div>"
198:             Visible="True" />
199:         </ItemTemplate>
202:       </asp:TemplateColumn>
203:       <asp:BoundColumn ItemStyle-Width="20"
204:         DataField="CustomerID"
205:         HeaderText="<div class='dgrdHeader'>ID</div>">
207:       </asp:BoundColumn>
           
・・・

228:     </Columns>

 

238-243では、TextBoxを定義しています。このTextBoxは、メッセージを表示するのと、Webページをポストバックさせる役割をします。

 

238: <asp:TextBox id="txtMessage" runat="server"
241:   Columns="107"
242:   Visible="True"
243:   ReadOnly="True" />

 

248-251では、Buttonを定義しています。このButtonWebページをポストバックさせる役割をします。

 

248: <asp:Button id="btnRefresh" runat="server"
249:   Text="Hidden"
250:   Visible="False"
251:   OnClick="btnRefresh_Click" />

 

リスト DataGridUpdate.aspxのソースコード(HTML編)

132: <html>
138: <body scroll="no">
147: <form id="frmMain" runat="server">
151: <asp:ImageButton id="ibtnUpdate" runat="server"
152:   CommandName="update"
153:   OnCommand="ibtnUpdate_Command"
154:   ImageUrl="../img/update.gif"
157:   ImageAlign="Middle" />
158: <asp:Label id="lblRows" runat="server"
159:   Text="
件数"
160:   CssClass="rowNumber" />
161: <asp:TextBox id="txtRows" runat="server"
163:   Columns="3"
164:   ReadOnly="True" />
177: <asp:DataGrid id="dgrdCustomers" runat="server"
178:   AutoGenerateColumns="False"
179:   OnItemCommand="dgrdCustomers_ItemCommand"
180:   OnItemDataBound="dgrdCustomers_ItemDataBound"
181:   OnItemCreated="dgrdCustomers_ItemCreated"
186:   EnableViewState="True">
       
・・・

191:   <Columns>
192:     <asp:TemplateColumn
193:       HeaderText="<div class='dgrdHeaderBox'>1</div>">
194:       <ItemTemplate>
195:         <asp:LinkButton id="lbtnSelect" runat="server"
196:           CommandName="Select"
197:           Text="<div class='dgrdItemArrow'>4</div>"
198:           Visible="True" />

199:       </ItemTemplate>
202:     </asp:TemplateColumn>
203:     <asp:BoundColumn
204:       DataField="CustomerID"
205:       HeaderText="<div class='dgrdHeader'>ID</div>">
207:     </asp:BoundColumn>
          
・・・

228:   </Columns>
229: </asp:DataGrid>
238: <asp:TextBox id="txtMessage" runat="server"
241:   Columns="107"
242:   Visible="True"
243:   ReadOnly="True" />
248: <asp:Button id="btnRefresh" runat="server"
249:   Text="Hidden"
250:   Visible="False"
251:   OnClick="btnRefresh_Click" />
252: </form>
253: </body>
253: </html>

 

◆メインプログラムDataGridUpdate.aspxの解説(コード編)

 

DataGridUpdate.aspxは、DataGrid上に得意先テーブルを表示します。DataGridからレコードを選択して、更新ボタンをクリックすると、子ウィンドウを開きます。また、子ウィンドウと連携して、子ウィンドウから編集したレコードをDataGridに表示する処理も行っています。

 

Sub Page_Load()イベントの処理

 

このイベントは、DataGridUpdate.aspxがロードされたときに実行されます。このイベントでは、クライアント側で動作するイベントの登録と、DataGridに得意先テーブルをバインドします。

 

11では、メッセージを表示するTextBoxにクライアント側で動作する、onPropertyChangeイベントを登録しています。onProperyChangeイベントでは、Webページをポストバックします。GetPostBackEventReference()メソッドは、WebページをポストバックするJavaScriptを生成します。Webページがポストバックされると、btnRefreshイベントが実行されます。

 

12-21If…Else…End Ifでは、ページが最初にロードされたか調べています。ページが最初にロードされたときは、ViewStateDataGridから選択した行の得意先IDを保存します。ページがポストバックされたときは、ViewStateから得意先IDを取得して変数に保存します。さらに、Session変数に保存されているDataTableを取得して変数に退避します。Session変数にDataTableが保存されていないときは、BindDataGrid()を呼び出します。BindDataGridでは、得意先テーブルのDataTableを作成してDataGridにバインドします。

 

10: Sub Page_Load()
 11:   txtMessage.Attributes.Add("OnPropertyChange", GetPostBackEventReference(btnRefresh))
 12:   If Not IsPostBack Then
 13:     ViewState("CustomerID") = mintCustomerID
 14:     BindDataGrid()
 15:   Else
 16:     mintCustomerID = ViewState("CustomerID")
 17:     mdt = CType(Session("Customers"), DataTable)
 18:     If mdt Is Nothing Then
 19:       BindDataGrid()
 20:     End If
 21:   End If
 22: End Sub

 

Sub dgrdCustomers_ItemDataBound()イベントの処理

 

このイベントは、DataGridDataBind()メソッドが実行されたときに発生します。このイベントでは、DataGridのアイテム(DataGridItem)にクライアント側で動作するonClickイベントを登録しています。これにより、DataGridの任意のセルをクリックして行を選択できるようになります。

 

38: Sub dgrdCustomers_ItemDataBound(s As Object, e As DataGridItemEventArgs)
 39:   Dim lit As ListItemType = e.Item.ItemType
 40:  
 41:   If lit = ListItemType.Item OrElse _
 42:     lit = ListItemType.AlternatingItem OrElse _
 43:     lit = ListItemType.SelectedItem Then
 44:     Dim lbtn As LinkButton = CType(e.Item.FindControl("lbtnSelect"), LinkButton)
 45:     e.Item.Attributes("onClick") = GetPostBackClientHyperlink(lbtn, "")
 46:     e.Item.Style("cursor") = "hand"
 47:   End If
 48: End Sub

 

Sub dgrdCustomers_ItemCommand()イベントの処理

 

このイベントは、DataGridから行をクリックしたときに発生します。このイベントでは、DataGridから選択した行の情報をViewStateに保存します。

 

52では、DataGridDataKeysコレクションから選択された行の主キー(得意先ID)を取得しています。Item.ItemIndexには、選択されたアイテムのインデックス番号が格納されています。行53では、得意先IDViewStateに保存しています。ViewStateに保存した得意先IDは、レコード更新ボタンをクリックしたときに参照します。

 

50: Sub dgrdCustomers_ItemCommand(s As Object, e As DataGridCommandEventArgs)
 51:   If e.CommandName = "Select" Then
 52:     mintCustomerID = dgrdCustomers.DataKeys(e.Item.ItemIndex)
 53:     ViewState("CustomerID") = mintCustomerID
 55:   End If
 56: End Sub

 

Sub ibtnUpdate_Command()イベントの処理

 

このイベントは、Webページからレコードの更新ボタンをクリックしたときに発生します。このイベントでは、レコード更新用の新規ウィンドウを表示します。

 

59-61If…End Ifでは、DataGridから更新するレコードを選択しているか調べています。レコードが選択されているときは、InsertScriptBlock()関数を呼び出して新規ウィンドウを開きます。この関数の引数には、更新するレコードの得意先IDを指定します。

 

58: Sub ibtnUpdate_Command(s As Object, e As CommandEventArgs)
 59:   If dgrdCustomers.SelectedIndex <> -1 Then
 60:     InsertScriptBlock(mintCustomerID)
 61:   End If
 62: End Sub

 

Sub btnRefresh_Click()イベントの処理

 

このイベントは、クライアント側からメッセージを表示するTextBoxを書き換えたときに発生します。このサンプルでは、子ウィンドウから親ウィンドウのTextBoxにメッセージを設定して書き換えます。つまり、子ウィンドウから親ウィンドウをポストバックさせています。btnRefresh_Clickイベントでは、DataGridに得意先テーブルをバインドしてリフレッシュします。

 

24: Sub btnRefresh_Click(s As Object, e As EventArgs)
 25:   BindDataGrid()
 26:   With dgrdCustomers
 27:     .DataSource = mdt
 28:     .DataKeyField = "CustomerID"
 29:     .DataBind()
 30:   End With
 31:   txtRows.Text = mdt.Rows.Count.ToString()
 32: End Sub

 

Sub BindDataGrid()の処理

 

このサブプロシージャでは、DataGridに得意先テーブルをバインドして表示します。BindDataGridは、Page_Loadイベントから呼ばれます。

 

65では、得意先テーブルからレコードを抽出するSQLを生成しています。このSQLでは、Order By句で「CustomerID Desc」を指定していますので、得意先IDの降順にレコードが並べ替えられます。また、Top 10のオプションを指定していますので、上位10件のレコードが抽出されます。

 

66では、CreateDataSet()関数を呼び出して得意先テーブルのDataSetを作成しています。行67では、DataSetTablesコレクションからDataTableを作成しています。行68では、DataTablePrimaryKeyプロパティに得意先IDDataColumnを設定しています。PrimaryKeyに主キーを設定することにより、DataTableRowsコレクションのFind()メソッドを使用することができます。

 

69-76With…End Withでは、DataGridDataTableをバインドしています。行73では、DataGridSelectedIndexプロパティに0を設定して、DataGridの先頭行を選択した状態にしています。行74-75では、先頭行の得意先IDを取得してViewStateに保存しています。ここで保存したViewStateは、ポストバックされたときに参照します。

 

77では、件数のTextBoxにレコード件数を設定しています。行78では、Session変数にDataTableを保存しています。ここで保存したSession変数は、ポストバックされたときと、サブプログラムから参照します。

 

64: Sub BindDataGrid()
 65:   Dim strSQL As String = "Select top 10 * From Customers Order by CustomerID Desc"
 66:   Dim ds AS DataSet = CreateDataSet(strSQL)
 67:   mdt = ds.Tables(0)
 68:   mdt.PrimaryKey = New DataColumn() {mdt.Columns("CustomerID")}
 69:   With dgrdCustomers
 70:     .DataSource = mdt
 71:     .DataKeyField = "CustomerID"
 72:     .DataBind()
 73:     .SelectedIndex = 0
 74:     mintCustomerID = .DataKeys(0)
 75:     ViewState("CustomerID") = mintCustomerID
 76:   End With
 77:   txtRows.Text = mdt.Rows.Count.ToString()
 78:   Session("Customers") = mdt
 79: End Sub

 

Sub InsertScriptBlock()の処理

 

このサブプロシージャでは、新規ウィンドウを開くJavaScriptを生成して登録します。InsertScriptBlockは、レコードの更新ボタンをクリックしたときに、更新ボタンのOnCommandイベントから呼ばれます。

 

82-84では、JavaScriptwindow.open()メソッドの引数に指定するオプションを生成しています。行86-90With…End Withでは、StringBuilderAppend()メソッドで以下のJavaScriptを生成しています。

 

<script language='javascript'>
window.open('PopupUpdate.aspx?id=9999','_blank','features');

</script>

 

JavaScriptwindow.open()メソッドは、新規ウィンドウを開きます。Open()メソッドの引数には、urltargetfeaturesを指定します。urlには、新規ウィンドウに表示するファイルPopupUpdate.aspxを指定します。?id=9999は、QueryStringと呼ばれるパラメータです。このサンプルでは、親ウィンドウから子ウィンドウにデータを渡すのにQueryStringを使用します。Id=には、更新するレコードの得意先IDを指定します。

 

91では、Page.RegisterClientScriptBlock()メソッドでJavaScriptを登録します。ここで登録したJavaScriptは、Webページがロードされたときにクライアント側のブラウザから実行されます。

 

81: Sub InsertScriptBlock(intCustomerID As Integer)
 82:   Dim strFeatures As String = "height=200,width=330,left=10,top=10," & _
 83:     "location=no,menubar=no,resizable=yes,scrollbars=no," & _
 84:     "status=no,titlebar=yes,toolbar=no"
 85:   Dim sbScript As New StringBuilder()
 86:   With sbScript
 87:     .Append("<script language='javascript'>" & vbCrLf)
 88:     .Append(vbTab & "window.open('PopupUpdate.aspx?id=" &

intCustomerID.ToString & "','_blank','" & strFeatures & "');" & vbCrLf)
 89:     .Append("</" & "script>")
 90:   End With
 91:   RegisterClientScriptBlock("openWindow", sbScript.ToString)
 92: End Sub

 

Sub InsertAlertScript()の処理

 

このサブプロシージャでは、エラーメッセージをポップアップウィンドウに表示するJavaScriptを生成して登録します。InsertAlertScriptは、CreateDataSet()関数からエラーが発生したときに呼ばれます。ここで登録したJavaScriptは、Webページがロードされたときにクライアント側のブラウザから実行されます。

 

 

94: Sub InsertAlertScript(strMessage As String)
 95:   Dim strMsg As String = strMessage.Replace("'","\'")
 96:   Dim sbScript As New StringBuilder()
 97:   With sbScript
 98:     .Append("<script language='javascript'>" & vbCrLf)
 99:     .Append(vbTab & "alert('" & strMsg & "');" & vbCrLf)
101:     .Append("</" & "script>")
102:   End With             
103:   RegisterClientScriptBlock("alertScript", sbScript.ToString)
104: End Sub

 

Function CreateDataSet()関数の処理

 

この関数は、DataSetを作成して返します。CreateDataSetは、BindDataGrid()から呼ばれます。

 

117-118では、Connectionのインスタンスを生成しています。Connectionの引数には、Web.Configに登録されているデータベース接続文字列を指定しています。Web.Configに登録されている内容を取得するには、ConfigurationSettingsAppSettings()メソッドを使用します。AppSettings()メソッドの引数には、<add>タグのkeyを指定します。

 

117:   Dim con As New OleDbConnection( _
118:     ConfigurationSettings.AppSettings("
conStringAccNw"))

<appSettings>

  <add key="conStringAccNw"

   value="PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA Source=C:\WebMatrix\webdb\Nwind.mdb" />                         

</appSettings>

 

119では、DataAdapterのインスタンスを生成しています。DataAdapterの引数には、SQLConnectionオブジェクトを指定しています。

 

121-126Try…Catch…End Tryブロックでは、DataAdapterFill()メソッドでSQLを実行して得意先テーブルをDataSetに取り込んでいます。行121-122Tryブロックでエラーが発生したときは、行123-125Catchブロックが実行されます。Catchブロックでは、InsertAlertScript()を呼び出してポップアップウィンドウにエラーメッセージを表示します。

 

121:   Try
122:     da.Fill(ds)
123:   Catch e As Exception
124:     InsertAlertScript(e.Message)
125:     Return Nothing
126:   End Try

127では、関数の戻り値としてDataSetを返します。

 

115: Function CreateDataSet(strSQL As String, _
116:   Optional strConnectionString As String = "conStringAccNw") As DataSet
117:   Dim con As New OleDbConnection( _
118:     ConfigurationSettings.AppSettings(strConnectionString))
119:   Dim da As New OleDbDataAdapter(strSQL, con)
120:   Dim ds As New DataSet()
121:   Try
122:     da.Fill(ds)
123:   Catch e As Exception
124:     InsertAlertScript(e.Message)
125:     Return Nothing
126:   End Try
127:   Return ds
128: End Function

 

 

リスト DataGridUpdate.aspxのソースコード(コード編)

  1: <%@ Page language="vb" SmartNavigation="false" %>
  2: <%@ Import Namespace="System.Data" %>
  3: <%@ Import Namespace="System.Data.OleDb" %>
  4:
  5: <script language="vb" runat="server">
  6: Private mdt As DataTable
  7: Private mintCustomerID As Integer = 0
  8: Private mintBookMarkID As Integer = 0
  9:
 10: Sub Page_Load()
 22: End Sub
 23:
 24: Sub btnRefresh_Click(s As Object, e As EventArgs)
 32: End Sub
 33:
 34: Sub dgrdCustomers_ItemCreated(s As Object, e As DataGridItemEventArgs)
 36: End Sub
 37:
 38: Sub dgrdCustomers_ItemDataBound(s As Object, e As DataGridItemEventArgs)
 48: End Sub
 49:
 50: Sub dgrdCustomers_ItemCommand(s As Object, e As DataGridCommandEventArgs)
 56: End Sub
 57:
 58: Sub ibtnUpdate_Command(s As Object, e As CommandEventArgs)
 62: End Sub
 63:
 64: Sub BindDataGrid()
 79: End Sub
 80:
 81: Sub InsertScriptBlock(intCustomerID As Integer)
 92: End Sub
 93:
 94: Sub InsertAlertScript(strMessage As String)
104: End Sub
105:
115: Function CreateDataSet(strSQL As String, _
128: End Function
130: </script>

 

 

◆サブプログラムPopupUpdate.aspxの解説(HTML編)

 

子ウィンドウのWebフォームには、TextBoxButtonを作成しています。TextBoxには、得意先レコードが表示されますので編集することができます。

 

105-182<fieldset>…</fieldset>では、角の丸みがかったボックスを表示しています。行106-110<legend>…</legend>では、ボックスの凡例を表示しています。

 

112-124<tr>…</tr>では、得意先IDLabelTextBoxを定義しています。TextBoxには、得意先レコードの得意先IDが表示されます。得意先IDは変更することができませんので、TextBoxReadOnlyプロパティにTrueを設定して読み込み専用にしています。

 

126-140<tr>…</tr>では、得意先名のLabelTextBoxを定義しています。行136-138では、RequiredFieldValidatorで得意先名が入力されているかチェックしています。得意先名を入力しないで更新ボタンをクリックすると、TextBoxの右側に「*」が表示されます。

 

後続する<tr>…</tr>では、担当者名、部署役職、電話番号のLabelTextBoxを定義しています。

 

185-225<table>…</table>には、ImageButtonLabelDropDownListButtonを定義しています。ImageButtonDropDownListEnabledプロパティには、Falseを設定して使用不可にしています。このサンプルでは、これらの機能はサポートしていません。行215-222では、「更新」と「閉じる」のボタンを定義しています。「閉じる」のボタンには、CausesValidationプロパティにFalseを設定してRequiredFieldValidatorを適用させないようにしています。

 

リスト  PopupUpdate.aspxのソースコード(HTML編)

97: <html>
103: <body>
104: <form id="frmPopupUpdate" runat="server">
111: <table>
112: <tr>
113: <td>
114:   <asp:Label id="lblCustomerID" runat="server"
115:     CssClass="label"
116:     Text="ID" />
117: </td>
118: <td>
119:   <asp:TextBox id="txtCustomerID" runat="server"
120:     CssClass="textBox"
121:     Text="auto"
122:     Columns="4" ReadOnly="True" />
123: </td>
124: </tr>
126: <tr>
127: <td>
128:   <asp:Label id="lblCompanyName" runat="server"
129:     CssClass="label"
130:     Text="
得意先名" />
131: </td>
132: <td>
133:   <asp:TextBox id="txtCompanyName" runat="server"
134:     CssClass="textBox"
135:     Columns="40" />
136:   <asp:RequiredFieldValidator runat="server"
137:     ControlToValidate="txtCompanyName"
138:     ErrorMessage="*" />
139: </td>
140: </tr>
    
・・・
181: </table>
215: <asp:Button id="btnUpdate" runat="server"
216:   Text="
更新"
218:   OnClick="btnUpdate_Click" />
219: <asp:Button id="btnClose" runat="server"
220:   CausesValidation="False"
221:   Text="
閉じる"
222:   />
227: </form>
228: </body>
228: </html>

 

 

  サブプログラムPopupUpdate.aspxの解説(コード編)

 

PopupUpdate.aspxは、Webフォームから編集したレコードを2段階でデータベースに反映します。Webフォームから編集したレコードは、一旦DataTable上で更新されます。さらに、DataTableの変更されたレコードをデータベースに反映します。編集したレコードをデータベースに反映したら、親ウィンドウをポストバックさせてDataGrid上に表示します。

 

Sub Page_Load()イベントの処理

 

このイベントは、ページがロードされたときに発生します。Page_Loadでは、ページの初期化処理を行います。

 

9では、Session変数に保存されている得意先テーブルのDataTableを取得して変数に退避します。

 

10-14If…EndIfでは、ページが最初にロードされたか調べています。最初にロードされたときは、「閉じる」ボタンにクライアント側で動作するonClickイベントを登録しています。onClickイベントでは、JavaScriptwindow.close()メソッドを実行して子ウィンドウを閉じます。次に、AddCursor()を呼び出してアプリケーション独自のカーソルを表示しています。最後に、DisplayRecord()を呼び出してTextBoxに得意先レコードを表示します。

 

  8: Sub Page_Load()
  9:   mdt = CType(Session("Customers"), DataTable)
 10:   If Not IsPostBack Then
 11:     btnClose.Attributes.Add("onclick", "window.close();")
 12:     AddCursor()
 13:     DisplayRecord()
 14:   End If
 15: End Sub

 

Sub btnUpdate_Click()イベントの処理

 

このイベントは、更新ボタンをクリックしたときに発生します。このイベントでは、得意先テーブルのレコードを更新します。また、ここで更新したレコードを親ウィンドウのDataGridに反映させます。

 

18では、UpdateRecord()関数を呼び出して得意先テーブルのレコードを更新します。UpdateRecordからは、処理の結果がメッセージで返されます。行20-28With…End Withでは、StringBuilderAppend()メソッドで以下のJavaScriptを生成しています。

 

<script language='javascript'>

  window.opener.frmMain.txtMessage.value = 'XXX';

alert('同時実行違反発生!');

</script>

 

JavaScriptwindow.opener.frmMain.txtMessage.value=は、親ウィンドウのtxtMessage.valueにメッセージを設定します。txtMessageには、クライアント側で動作するonProperyChangeイベントが登録されていますので、このイベントが実行されます。onPropertyChangeイベントには、WebページをポストバックさせるJavaScriptが記述されていますので、WebページがポストバックされてDataGridがリフレッシュされます。これで親ウィンドウのDataGridには、子ウィンドウから更新したレコードが表示されます。

 

JavaScriptalert()関数は、「同時実行違反」のエラーが発生したときに生成されます。alert()関数は、ポップアップウィンドウにエラーメッセージを表示します。

 

29では、Page.RegisterClientScriptBlock()メソッドでJavaScriptを登録しています。ここで登録した、JavaScriptは、Webページがロードされたときクライアント側のブラウザから実行されます。

 

17: Sub btnUpdate_Click(s As Object, e As EventArgs)
 18:   Dim strMessage As String = UpdateRecord()             
 19:   Dim sbScript As New StringBuilder()
 20:   With sbScript
 21:     .Append("<script language='javascript'>" & vbCrLf)
 22:     .Append(vbTab & "window.opener.frmMain.txtMessage.value = '" & _
 23:       strMessage & "';" & vbCrLf)
 24:     If strMessage = "
同時実行違反発生!" Then
 25:       .Append(vbTab & "alert('" & strMessage & "');" & vbCrLf)
 26:     End If
 27:     .Append("</" & "script>")
 28:   End With
 29:   RegisterClientScriptBlock("update", sbScript.ToString)
 30: End Sub

 

 

Sub AddCursor()の処理

 

このサブプロシージャでは、アプリケーション独自のカーソルを追加します。AddCursorは、Page_Loadイベントからページが最初にロードされたときに呼ばれます。

 

カーソルを追加するには、TextBoxにクライアント側で動作するonFocusonBlurイベントを登録します。onFocusイベントは、TextBoxがフォーカスを取得したときに発生します。onBlurは、フォーカスを喪失したときに発生します。これらのイベントで、TextBoxの背景色を書き換えることによりアプリケーション独自のカーソルを表示することができます。

 

TextBoxにクライアント側で動作するイベントを登録するには、AttributesコレクションのAdd()メソッドを使用します。Add()メソッドの引数には、イベント名とイベント処理を記述します。

 

onFocusイベントでは、TextBoxの背景色をLightGreenに書き換えます。onBlueイベントでは、TextBoxの背景色をAliceBlueに戻します。

 

44: Sub AddCursor()
 45:   txtCompanyName.Attributes.Add("onFocus","this.style.backgroundColor='LightGreen'")
 46:   txtCompanyName.Attributes.Add("onBlur","this.style.backgroundColor='AliceBlue'")
 47:
 48:   txtContactName.Attributes.Add("onFocus","this.style.backgroundColor='LightGreen'")
 49:   txtContactName.Attributes.Add("onBlur","this.style.backgroundColor='AliceBlue'")
 50:
 51:   txtContactTitle.Attributes.Add("onFocus","this.style.backgroundColor='LightGreen'")
 52:   txtContactTitle.Attributes.Add("onBlur","this.style.backgroundColor='AliceBlue'")
 53:
 54:   txtPhone.Attributes.Add("onFocus","this.style.backgroundColor='LightGreen'")
 55:   txtPhone.Attributes.Add("onBlur","this.style.backgroundColor='AliceBlue'")
 56: End Sub

 

 

Sub DisplayRecord()の処理

 

このサブプロシージャでは、TextBoxに得意先テーブルのレコードを表示します。DisplayRecordは、Page_Loadイベントからページが最初にロードされたときに呼ばれます。

 

33では、RequestParamsコレクションからQueryStingに指定しているパラメータを取得しています。QueryStringには、親ウィンドウのDataGridから選択したレコードの得意先IDが指定されています。Request.Params()の代わりにRequest.QueryString()を使用することもできます。

 

PupupUpdate.aspx?id=9999

Request.Params("id")                     è 9999

Request.QueryString("id")             è 9999

 

34では、DataTableRowsコレクションのFind()メソッドで得意先のレコードを検索します。Find()メソッドの引数には、DataTableの主キー(得意先ID)を指定します。Find()メソッドからは、DataRowが返されます。

 

35-41If…End Ifでは、レコードが見つかったか調べています。レコードが見つかったときは、DataRowから得意先ID、得意先名、担当者名、部署役職、電話番号を取得してTextBoxに表示します。担当者名、役職部署、電話番号は、Nullの可能性があるためDataRowIsNull()メソッドで調べています。Nullのときは、空白を表示します。

 

32: Sub DisplayRecord()
 33:   Dim intCustomerID As Integer = Int32.Parse( Request.Params("id") )
 34:   Dim dr As DataRow = mdt.Rows.Find(intCustomerID)
 35:   If Not (dr Is Nothing) Then
 36:     txtCustomerID.Text = dr("CustomerID")
 37:     txtCompanyName.Text = dr("CompanyName")
 38:     txtContactName.Text = Iif(dr.IsNull("ContactName"), "", dr("ContactName"))
 39:     txtContactTitle.Text = Iif(dr.IsNull("ContactTitle"), "", dr("ContactTitle"))
 40:     txtPhone.Text = Iif(dr.IsNull("Phone"), "", dr("Phone"))
 41:   End If
 42: End Sub

 

 

Function UpdateRecord()関数の処理

 

この関数では、DataTableのレコードを更新します。UpdateRecord()は、更新ボタンのOnClickイベントから呼ばれます。

 

60-64では、TextBoxから得意先ID、得意先名、担当者名、部署役職、電話番号を取得しています。行67では、DataTableRowsコレクションのFind()メソッドで得意先テーブルのレコードを検索しています。Find()メソッドの引数には、主キー(得意先ID)を指定します。Find()メソッドからは、DataRowが返されます。行68-73では、DataRowBeginEditEndEditで得意先名、担当者名、部署役職、電話番号のカラムを更新しています。

 

75では、UpdateDataTable()関数を呼び出して、DataTableの変更されたレコードをデータベースに反映しています。

 

ここでは、DataRowのカラムを更新するときBeginEdit/EndEidtを使用していますが直接カラムを更新することもできます。BeginEditは、途中で更新をキャンセルするときに使用します。BeginEditを使用しないでカラムを更新したときは、途中でキャンセルすることはできません。

 

dr.BeginEdit

  dr("CompanyName") = strCompanyName

  dr("ContactName") = strContactName

  dr("ContactTitle") = strContactTitle

  dr("Phone") = strPhone

dr.EndEdit or dr.CancelEdit è 更新を確定、またはキャンセルする

 

58: Function UpdateRecord() As String
 59:   Dim strMessage As String
 60:   Dim intCustomerID As Integer = Int32.Parse( txtCustomerID.Text )
 61:   Dim strCompanyName As String = txtCompanyName.Text.Trim()
 62:   Dim strContactName As String = txtContactName.Text.Trim()
 63:   Dim strContactTitle As String = txtContactTitle.Text.Trim()
 64:   Dim strPhone As String = txtPhone.Text.Trim()
 65:   Dim strSQL As String = "Select CustomerID, CompanyName, ContactName,

ContactTitle, Phone From Customers"
 67:   Dim dr As DataRow = mdt.Rows.Find(intCustomerID)
 68:   dr.BeginEdit
 69:     dr("CompanyName") = strCompanyName
 70:     dr("ContactName") = strContactName
 71:     dr("ContactTitle") = strContactTitle
 72:     dr("Phone") = strPhone
 73:   dr.EndEdit
 74:
 75:   Return UpdateDataTable(strSQL)
 76: End Function

 

 

Function UpdateDataTable()関数の処理

 

この関数では、DataTableの変更されたレコードをデータベースに反映します。この関数は、UpdateRecord()から呼ばれます。UpdateDataTable()の引数には、strSQLstrConnectionStringを指定します。strSQLには、得意先テーブルからレコードを抽出するSQLを指定します。strConnectionStringには、データベースの接続文字列を指定します。DataTableをデータベースに反映するには、DataAdapterUpdate()メソッドを使用します。Update()メソッドで使用するINSERTUPDATEDELETESQLは、CommandBuilderで自動生成します。

 

80-81では、Connectionのインスタンスを生成しています。Connectionの引数には、データベースの接続文字列を指定します。行82では、DataAdapterのインスタンスを生成しています。DataAdapterの引数には、SQLConnectionのオブジェクトを指定します。引数のSQLには、SELECTステートメントを指定します。行83では、CommandBuilderDataTableをデータベースに反映するためのSQLを自動生成します。CommandBuilderは、SELECTステートメントを元にINSERTUPDATEDELETESQLを生成します。

 

86-91Try…Catch…End Tryブロックでは、DataAdapterUpdate()メソッドでDataTableの変更されたレコードをデータベースに反映します。DataTableに新規レコードが追加されたときは、SQLINSERTステートメントを実行してDataTableのレコードをデータベースに追加します。DataTableのレコードが更新されたときは、UPDATEステートメントで、データベースのレコードを更新します。DataTableのレコードが削除されたときは、DELETEステートメントで、データベースからレコードを削除します。データベースのレコードを更新、削除するときは、他のユーザから変更されていないことを確認してから実行します。他のユーザから変更されているときは、同時実行違反のエラーになります。

 

Update()メソッドでエラーが発生したときは、行89-90Catchブロックが実行されます。行92では、戻り値として処理の結果を示すメッセージを返します。

 

同時実行違反のエラーが発生したときは、ポップアップウィンドウに「同時実行違反発生!」のエラーメッセージが表示されます。このとき、親ウィンドウのDataGridには最新のデータが表示されますので、子ウィンドウのデータと比較することができます。子ウィンドウから再度、更新ボタンをクリックしたときは、親ウィンドウにてDataTableをリフレッシュしていますので正常に更新されます。

 

78: Function UpdateDataTable(strSQL As String, _
 79:   Optional strConnectionString As String = "conStringAccNw") As String
 80:   Dim con As New OleDbConnection( _
 81:     ConfigurationSettings.AppSettings(strConnectionString))
 82:   Dim da As New OleDbDataAdapter(strSQL, con)
 83:   Dim cb As New OleDbCommandBuilder(da)
 84:   Dim strMessage As String
 85:
 86:   Try
 87:     da.Update(mdt)
 88:     strMessage = "
正常終了!"
 89:   Catch
 90:     strMessage = "
同時実行違反発生!"             
 91:   End Try
 92:   Return strMessage
 93: End Function

 

 

リスト  PopupUpdate.aspxのソースコード(コード編)

  1: <%@ Page language="vb" SmartNavigation="False" %>
  2: <%@ Import Namespace="System.Data" %>
  3: <%@ Import Namespace="System.Data.OleDb" %>
  4:
  5: <script language="vb" runat="server">
  6: Private mdt As DataTable
  7:
  8: Sub Page_Load()
 15: End Sub
 16:
 17: Sub btnUpdate_Click(s As Object, e As EventArgs)
 30: End Sub
 31:
 32: Sub DisplayRecord()
 42: End Sub
 43:
 44: Sub AddCursor()
 56: End Sub
 57:
 58: Function UpdateRecord() As String
 76: End Function
 77:
 78: Function UpdateDataTable(strSQL As String, _
 93: End Function
 95: </script>

 

Note

DataAdapterUpdate()メソッドで使用するUPDATEステートメントを手動にて作成して改善するには

 

DataAdapterSelectCommandに、以下のようなSELECTステートメントを設定して、CommandBuilderを実行すると、得意先レコードの得意先ID、得意先名、担当者名、部署役職、電話番号が他のクライアントから変更されていないことを確認してからレコードを更新します。

 

Dim strSQL As String = "Select CustomerID, CompanyName, ContactName,

ContactTitle, Phone From Customers"

 

Dim con As New OleDbConnection( _

  ConfigurationSettings.AppSettings(strConnectionString))

Dim da As New OleDbDataAdapter(strSQL, con)

Dim cb As New OleDbCommandBuilder(da)

 

CommandBuilderが生成したUPDATEステートメント:

UPDATE Customers SET CompanyName = ? , ContactName = ? ,

  ContactTitle = ? , Phone = ?

WHERE ( (CustomerID = ?)

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 Phone IS NULL) OR (Phone = ?)) )

 

DataAdapterSelectCommandに、Select * From CustomersのようなSELECTステートメントを設定してCommandBuilderを実行すると、得意先レコードのすべてのフィールドが変更されていないことを確認してからレコードを更新します。このように、レコードのフィールド数が多いときは、CommandBuilderが生成したUPDATEステートメントはあまり効率よくありません。

 

UPDATEステートメントで、すべてのフィールドをチェックする代わりにConcurrencyID、タイムスタンプなどを利用すると性能を改善することができます。この場合、CommandBuilderを使用しないで手動にてUPDATEステートメントを作成します。ConcurrencyIDには、レコードの更新回数を格納します。

 

da.UpdateCommand = CreateUpdateCommand()

 

Function CreateUpdateCommand() As OleDbCommand

  Dim sbSQL As New StringBuilder()

  With sbSQL

    .Append("Update Customers " & vbCrLf)

    .Append(" Set CompanyName = ?, " & vbCrLf)

    .Append("  ContactName = ?, " & vbCrLf)

  .Append("  ContactTitle = ?, " & vbCrLf)

    .Append("  Phone = ?, " & vbCrLf)

    .Append("  ConcurrencyID = ConcurrencyID + 1 " & vbCrLf)

    .Append(" Where CustomerID = ? And " & vbCrLf)

    .Append("  ConcurrencyID = ? ")

  End With 

  Dim cmd As New OleDbCommand(sbSQL.ToString, mcon)

  Dim pc As OleDbParameterCollection = cmd.Parameters

  Dim param As OleDbParameter

  With pc

    .Add("@NewCompanyName", OleDbType.VarWChar, 40, "CompanyName")

    .Add("@NewContactName", OleDbType.VarWChar, 30, "ContactName")

   .Add("@NewContactTitle", OleDbType.VarWChar, 30, "ContactTitle")

    .Add("@NewPhone", OleDbType.VarWChar, 24, "Phone")

   

    param = .Add("@OrgCustomerID", OleDbType.Integer, 0, "CustomerID")

    param.SourceVersion = DataRowVersion.Original

    param = .Add("@OrgConcurrencyID", OleDbType.Integer, 0, "ConcurrencyID")

    param.SourceVersion = DataRowVersion.Original

  End With

  Return cmd

End Function

 

手動にて作成したUPDATEステートメントのWHERE句では、CustomerIDConcurrencyIDをチェックするだけで、レコードが変更されていないことを確認することができますので性能が改善されます。

 

Update Customers

 Set CompanyName = ?, ContactName = ?, ContactTitle = ?, Phone = ?,

  ConcurrencyID = ConcurrencyID + 1

 Where CustomerID = ? And ConcurrencyID = ?

 

DataRowVersion.Originalは、WHERE句のパラメータ@OrgCustomerID@OrgConcurrencyIDに変更前の値を代入することを意味します。

 

param = .Add("@OrgCustomerID", OleDbType.Integer, 0, "CustomerID")

param.SourceVersion = DataRowVersion.Original

param = .Add("@OrgConcurrencyID", OleDbType.Integer, 0, "ConcurrencyID")

param.SourceVersion = DataRowVersion.Original

 

 

 

Tip

ViewStateSession変数の違い

 

ViewState()は、同じWebページ内で情報を渡すときに使用します。Session()変数は、Webページ間で情報を渡すときに使用します。Webページ間で情報を渡すには、この他にQueryStringResponse.Transfer()メソッドを使用する方法もあります。

 

ViewState()を使用するときは、大文字/小文字を別々に認識しますので注意してください。

 

ViewState("Name") = "Friendly Soft"  è Friendly Soft

ViewState("name") = "firendly soft"   è friendly soft

 

 

 

Tip

更新ボタンをクリックしたときに確認の問い合わせをするには(Validatorコントロールも使用可)

 

更新ボタンに確認の問い合わせを追加するには、Page_Loadイベントに以下のコードを追加します。

 

btnUpdate.Attributes.Add("onclick", "return confirm(更新してよろしいですか?);")

 

ところが、RequiredFieldValidatorなどのValidatorコントロールを使用しているときは、更新ボタンのonClickイベントが競合するために正常に動作しません。この不都合を回避するには、更新ボタンを<span>…</span>タグで囲って、onClickイベントを追加します。

 

<span onClick="return confirm('更新してよろしいですか?');">

  <asp:Button id="btnUpdate" runat="server"

    Text="更新"

    Style="vertical-align:middle"

    OnClick="btnUpdate_Click" />

</span>

 

これで、更新ボタンをクリックすると確認のメッセージが表示されます。ところが、Validatorコントロールでエラーを検出したときは、エラーメッセージが表示される前に確認のメッセージが表示されます。Webフォームにエラーが無いときのみ確認のメッセージを表示するには、confirm()メソッドの代わりにconfirmUpdate()関数を呼びます。これで、エラーが無いときのみ問い合わせのメッセージが表示されます。confirmUpdate()関数は、<head>…</head>タグに挿入するか、Page.RegisterClientScriptBlock()メソッドでランタイム時に登録します。

 

function confirmUpdate() {

  if (typeof(Page_ClientValidate) != 'function' || Page_ClientValidate()) {

    return confirm('更新してよろしいですか?');

  }

  else {

    return false;

  }

}

</script>

 

<span onClick="return confirmUpdate();">

  <asp:Button id="btnUpdate" runat="server"

    Text="更新"

    Style="vertical-align:middle"

    OnClick="btnUpdate_Click" />

</span>

 

 

ASP.NET DataGrid2のホームへ戻る