自動保存(AutoSave)機能の実装
2つ目の問題は、1つ目よりも少し厄介です。BindingSourceコンポーネントには「データが修正されたかどうかを示すフラグ」がないため、これが大きな問題となっています。グリッドの場合は、RowLeaveイベントの際にIsRowDirtyフラグを確認することができますが、バインドの詳細に関するフォーマットコントロールには、それに相当するフラグがありません(このことをバグとして報告したところ、Microsoftからは、将来のバージョンで修正予定であるという返事が来ました)。というわけで、今回作成するコントロールでは、データが編集されたときにセットできるフラグを追加する必要がありました。
- 画面上で右クリックし、[View Code]をクリックしてコードビハインドページ(分離コードページ)を開きます。次のコードを追加して、このコントロールに必要となる
IsDataDirtyプロパティを作成します。 - このフラグをセットするには、BindingSourceの
BindingCompleteイベントをインターセプトする必要があります。実際のところ、これは不経済なイベントです。というのは、レコードを変更するたびに、バインドされているコントロール1つ1つが呼び出されるためです。しかし、データが更新されてコントロールからデータセットに戻される際にもこのイベントが呼び出されます。つまり、本稿で必要となる情報を提供してくれるわけです。次のメソッドではBindingCompleteイベントを処理し、データが編集されている場合はプロパティをセットします。 - ここで
Handles句を使ってイベントをサブスクライブすることも可能ですが、今回のサンプルではVisual Basic内でエラーが発生するのを回避するために、BindingSourceが定義されるのを待ってからハンドラを割り当てるようにします。BindingSourceのsetメソッドのハンドラセクションに、次のコードを追加してください。 - 2行目で追加するハンドラによって、ユーザーが新しいレコードに移動したときに前のレコードを保存できるようになります。レコードがいつ変更されたかを知るために使うことのできるイベントは、いくつかあります。例えば、CurrentChanged、CurrentItemChanged、PositionChangedなどです。ここで最良の選択は、PositionChangedです。これらのイベントに関する問題は、どれもが「過去形」で実行されることです。つまり、「レコードが既に変更されてしまってからイベントが呼び出される」ということです。そのため、レコードを変更せずに残しておく手段がありません。最善の措置は、前のレコードのデータを保存することです。
- 次のメソッドでは、バインディングソースを対象に
PositionChangedイベントを処理し、フラグをチェックして、必要に応じて保存メソッドを呼び出します。
Private _IsDataDirty As Boolean = False Public Property IsDataDirty() As Boolean Get Return _IsDataDirty End Get Set(ByVal value As Boolean) _IsDataDirty = value If BindingNavigatorSaveItem.Enabled <> _IsDataDirty Then BindingNavigatorSaveItem.Enabled = _IsDataDirty End If End Set End Property
Private Sub BindingComplete(ByVal sender As Object, _ ByVal e As BindingCompleteEventArgs) 'is this a return from the control to the dataset? If e.BindingCompleteContext = BindingCompleteContext.DataSourceUpdate Then 'was it successful and not for a read only control? If e.BindingCompleteState = BindingCompleteState.Success And _ Not e.Binding.Control.BindingContext.IsReadOnly Then IsDataDirty = True End If End If End Sub
AddHandler _BindingSource.BindingComplete, AddressOf BindingComplete
AddHandler _BindingSource.PositionChanged, AddressOf bs_PositionChanged
Private Sub bs_PositionChanged(ByVal sender As Object, ByVal e As EventArgs) If (_IsDataDirty And Not _DataTable Is Nothing) Then SaveItem_Click(New Object(), New EventArgs()) End If End Sub
保存するかどうかをユーザーに確認する
3つ目の問題は、「変更されたデータを保存するか、変更前の状態に戻すか」という確認メッセージをユーザーに表示しなければならない場合が考えられるということです。もちろん、この確認メッセージの表示はオプション機能にすべきであるため、まずはAutoSaveというプロパティを追加する必要があります。これがfalseの場合は、保存するかどうかの確認メッセージがユーザーに表示されます。
- コードページのプロパティ定義の部分に次のコードを追加します。
- 前述の
PositionChangedイベント用のハンドラを修正して、このフラグをチェックし、フラグが有効である場合は保存確認メッセージを表示するようにします。 - これで保存確認ができるようになりましたが、レコードの削除の際にも、削除を確認する必要があります。そのためには、まず標準の削除メソッドを無効にする必要があります。ユーザーコントロールのデザイナビューで、BindingNavigatorのツールストリップを選びます。プロパティパネルの[Items]セクションにある[DeleteItem]でドロップダウンリストを表示して、[(none)]を選びます。
- さらに、ユーザーに確認メッセージを表示した後にのみレコードを削除するメソッドが必要です。ツールバーにある[Delete]アイコン(赤い×)をダブルクリックしてコードスタブを作成し、次のコードを追加します。
Private _AutoSaveFlag As Boolean Public Property AutoSaveFlag() As Boolean Get Return _AutoSaveFlag End Get Set(ByVal value As Boolean) _AutoSaveFlag = value End Set End Property
Private Sub bs_PositionChanged(ByVal sender As Object, ByVal e As EventArgs) _ Handles _BindingSource.PositionChanged If (_IsDataDirty And Not _DataTable Is Nothing) Then Dim msg As String = "Do you want to save edits to the previous record?" If _AutoSaveFlag Or MessageBox.Show(msg, "Confirm Save", _ MessageBoxButtons.YesNo) = DialogResult.Yes Then SaveItem_Click(New Object(), New EventArgs()) Else _DataTable.RejectChanges() MessageBox.Show("All unsaved edits have been rolled back.") _IsDataDirty=False End If End If End Sub

Private Sub BindingNavigatorDeleteItem_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles BindingNavigatorDeleteItem.Click Dim msg As String = "Are you sure you want to delete the current record? " If _AutoSaveFlag Or MessageBox.Show(msg, "Confirm Delete", _ MessageBoxButtons.YesNo) = DialogResult.Yes Then 'Delete the current record _BindingSource.RemoveCurrent() CType(_DataTable, Win._Interface.ITableUpdate).Update() End If End Sub
