この特集のトップページへ
Chapter 7:プレゼンテーション層の構築

7.4.3 顧客の一覧参照
●データグリッドとADODB.Recordsetオブジェクトとを結び付ける

○[最新の状態に更新]ボタンの処理
 さて,List 7-13の処理を実装すると,確かにデータグリッドに顧客情報が表示されるものの,サーバー上のデータベースが更新されても,それに伴い自動的にデータグリッドの内容も更新されるということはない。なぜなら,Business.CustomerコンポーネントのGetRecordsメソッドが返すADODB.Recordsetオブジェクトは,データベースから切り離されているからである。

 ここではとりあえず,顧客情報フォームに用意した[最新の情報に更新]ボタンを押した時点で,データグリッドの内容を更新するように実装してみることにしよう。データベースが更新された時点で自動的にデータグリッドの内容を表示するという処理は,次章以降で実装する。

 Fig.7-16で示したように,[最新の状態に更新]ボタンにはBTN_REFRESHという名前を付けた。そこで,BTN_REFRESHボタンのClickイベントに対応するよう,次のようなBTN_REFRESH_Clickプロシージャを実装すればよいことになる。


Private Sub BTN_REFRESH_Click()
    ' [最新の状態に更新]ボタンが押されたときの処理
    RefreshData
End Sub

 これは,単純にList 7-12に示したRefreshDataプロシージャを呼び出し,Business.CustomerコンポーネントのGetCustomersメソッドを呼び出して,その結果取得したADODB.Recordsetオブジェクトをデータグリッドに結び付けるという処理をするものである。Business.CustomerコンポーネントのGetCustomersメソッドを呼び出したときには,その時点での顧客情報を含むADODB.Recordsetオブジェクトが返されるから,結果としてデータグリッドに最新の情報が表示されることになる。

 この方法は悪くはないが,マーキーの位置(カレント行)が常にデータグリッドの先頭に来てしまうという問題を抱えている。なぜなら,新しく取得したADODB.Recordsetオブジェクトはカレント行の位置を先頭レコードとするので,それを指すようにデータグリッドのマーキーが再設定されるからである。

 よって,この実装ではユーザーが[最新の状態に更新]ボタンを押すたびに,マーキーが先頭に移動してしまうので,使いにくくなるという欠点がある。やはり,最新の状態に更新しても,マーキーの位置が変わらないほうがユーザーにとってわかりやすい。

 では,どのようにしてカレント行が変わらないようにすればよいのかという点だが,それには大きく2つの方法がある。1つは,ブックマークを利用する方法である。もう1つは,更新まえのカレント行が指している主キーとなっているフィールド値を保存しておき,更新後に保存しておいた主キーを備えたレコードをFindメソッドで検索し,見つかったレコードをカレント行とする方法である。

(1)ブックマークを使う方法

 まずは,ブックマークを使う方法から説明する。マーキーが指す行位置は,データグリッドのBookMarkプロパティに保存されている。その値を何らかの変数に保存しておいてからRefreshDataプロシージャを呼び出して,新しく取得したADODB.Recordsetオブジェクトをデータグリッドに結び付ける。その後,保存しておいたBookMarkプロパティの値をデータグリッドのBookMarkプロパティに設定すれば,カレント行の位置が更新まえの状態に戻る。実際にそのような処理を実装したものが,List 7-14である。

 List 7-14の6行目では,その時点のBookMarkプロパティの値を変数BookMarkに保存したのち,9行目でRefreshDataプロシージャを呼び出してデータグリッドの内容を最新の状態に更新する。そのあと16行目で,保存しておいた変数BookMarkの内容をBookMarkプロパティに設定している。

 なお,13行目でOn Error Resume Nextステートメントを使い,実行時エラーをトラップしている点に注目したい。このような処理をしている理由は,最新情報に更新したとき,更新まえにマーキーが指していた行が存在しなくなっていて移動できない可能性があるからである。つまり,マーキーが指している行をほかの誰かが削除するという可能性が否定できないのである。もし16行目の処理において保存しておいたブックマークの値をBookMarkプロパティの値として設定したときに,その行に移れないと実行時エラーが発生する。そのために,13行目のOn Error Resume Nextステートメントによる実行時エラーのトラップが必要となってくるのである。

 クライアント/サーバー環境では複数のユーザーがサーバーに接続して利用するために,自分以外の誰かがデータを書き換える可能性を考慮しておかないと,誤動作の原因になるので注意したい。

 ちなみに,16行目において実行時エラーが発生したときには,そのエラーはトラップされ,特にエラーは発生しない。しかし,ブックマークの復帰はされないから,マーキーは先頭行を指すことになる。これはやむを得ないだろう。

(2)Findメソッドを使う方法

 もう1つの方法は,更新まえにマーキーが指している行の主キーとなるフィールドの値を保存しておき,更新したあとで,保存しておいた値と同じ値を持つ行をカレント行として再設定する方法である。

 顧客情報テーブルの場合,IDフィールド(顧客番号)が主キーとなっている。たとえば,データグリッドの更新まえに,IDフィールドの値が3であるレコードをマーキーが示していたとする。このとき,データグリッドを更新したあとでIDフィールドの値が3であるレコードをカレント行にすれば,更新まえと更新後とで同じレコードをマーキーが指すようになる。

 ADODB.RecordsetオブジェクトにはFindというメソッドがあり,特定のフィールドが条件に合致するレコードをカレント行とする機能を持つ。FindメソッドはTable 7-7に示す書式で利用する。

Table 7-7 Findメソッド

【書式】

Find criteria, SkipRows, searchDirection, start

【引数】

  • criteria
    検索の条件式を指定する。「フィールド名演算子」の書式で指定する。利用できる演算子は,「=」「>」「<」「>=」「<=」「<>」「like」のみである。また,複数のフィールド名を使ったり,AND演算子やOR演算子を使って複数の条件を複合して指定したりすることはできない。

  • SkipRows
    検索を開始するオフセット行。省略可能。省略したときには0とみなされる。

  • searchDirection
    検索方法を指定する。adSearchForward定数(先頭から後尾に向かって)かadSearchBackword定数(後尾から先頭に向かって)のいずれかを指定する(省略可能)。値を省略したときにはadSearchForward定数とみなされる。

  • start
    検索の開始位置として利用するブックマークの値(省略可能)。値を省略したときには,カレント行からの検索となる。

 データグリッドに結び付けられているADODB.Recordsetオブジェクトは,グローバル変数g_objRecに格納している(List 7-12を参照)。よって,次のようにすれば,データグリッドの内容を更新したあとも,カレント行の位置を保つことができる。

Dim CustomerID As Long
' 現在のIDフィールドの値を取得する
CustomerID = g_objRec.Fields("ID").Value
' データグリッドの内容を更新する
RefreshData
' カレント行をIDフィールドがCustomerID変数に
' 格納した値に一致するものとする
g_objRec.MoveFirst
g_objRec.Find "ID=" & CustomerID, 0, adSearchForward

 実際にその処理を実装したものが,List 7-15である。

 ところで,List 7-15の15行目では,IDフィールドがCustomerID変数と一致するレコードを検索し,それをカレント行としていることになる。だが,該当するレコードが見つからなかった場合にはどうなるのだろうか。つまり,その時点のカレント行が,ほかの誰かによって削除されていたようなケースである。

 該当するレコードが見つからなかった場合,Findメソッドはカレント行をレコードの末尾よりも1つ後ろ(EOF)に移動させる(ただし,Findメソッドの第3引数にadSearchBackward定数が指定されているときには,先頭行よりも1つまえ(BOF)に移動させる)という仕様になっている。よって,更新まえにマーキーが指していたレコードが更新後になくなってしまった場合には,レコードの末尾よりも1つ後ろにカレント行が設定されるだけなので,特にエラーなどが発生することはない。ただし,カレント行でEOFの位置にある場合やBOFの位置にある場合には,データグリッドからマーキーが消えるので,もし,見つからないときにマーキーが先頭のレコードを指すようにしたいのであれば,16行目あたりに次のような処理を加えるとよい。

If g_objRec.EOF Then
    g_objRec.MoveFirst
End If

 EOFプロパティは,カレント行がレコードの末尾よりも後ろにある場合にTrueとなる。上記の処理によって,EOFプロパティがTrueであるとき,つまり,Findメソッドで該当するレコードが見つからなかった場合には,MoveFirstメソッドが実行されてカレント行が先頭に移動するため,マーキーは先頭行を指すようになる。

・ブックマークとFindメソッドのどちらの方法がよいのか

 では,List 7-14に示したブックマークを使う方法とList 7-15に示したFindメソッドを使う方法のどちらがよいのだろうか。

 どちらもほぼ同じ処理ではあるが,結論からいえば,List 7-15に示した処理のほうが望ましい。というのは,ブックマークの値はADODB.Recordsetオブジェクトごとに固有の値であり,別のADODB.Recordsetオブジェクトとは互換性がないためである。つまり,現在のADODB.Recordsetオブジェクトで取得したブックマークは,次にBusiness.CustomerコンポーネントのGetCustomersメソッドを呼び出して取得したADODB.Recordsetオブジェクトでは有効でないことがあるのだ。

 データベースエンジンにMSDEまたはSQL Serverを使っている場合,ADODB.Recordsetオブジェクトが含むレコードがまったく同じであれば,ブックマークの値も同じである。しかし,ADODB.Recordsetオブジェクトに含まれるレコードが変更されていた場合には,ブックマークの値は変わる。よって,List 7-14のプログラムは正しく動かない可能性がある。

 ブックマークは基本的に,同じADODB.Recordsetオブジェクトにおいて特定のレコードの場所を一時的に保存するために使われるもので,別のADODB.Recordsetオブジェクトとは互換性がない。その点,List 7-15に示したFindメソッドを使って主キーが特定の値であるレコードを検索するという方法では,主キーは必ず一意であるので,いかなるADODB.Recordsetオブジェクトを使ってアクセスしても必ず同じ結果が得られる。

 以上の理由から,今回はList 7-15の方法を採用することにする。

Prev 18/134 Next