ホーム.NET Study>[ CurrencyManager ( BindingManagerBase ) ] Windows フォームのデータ連結について

[ CurrencyManager ( BindingManagerBase ) ] Windows フォームのデータ連結について

【概要】

  CurrencyManager ( BindingManagerBase )を通して Windows フォームのデータ連結について検証します。
    ・検証1 : CurrencyManager ( BindingManagerBase )オブジェクトの共有について
    ・検証2 : カレントレコードの更新について
    ・検証3 : ユーザーコントロールのカスタムプロパティのデータ連結について

【検証1】 CurrencyManager ( BindingManagerBase )オブジェクトの共有について



 【MSDN Library の「Windows フォーム上のデータのコンシューマ」の「CurrencyManager クラス」 引用】
 Windows フォーム コントロールを連結するデータ ソースには、CurrencyManager オブジェクトが関連付けられます。CurrencyManager オブジェクトは、データ ソースの場所を追跡し、データ ソースへの連結を監視します。フォーム上には、連結先の各データ ソースに対して個別の CurrencyManager オブジェクトがあります。フォーム上のコントロールがすべて 1 つのソースに連結される場合 (たとえば、いくつかの TextBox コントロールが同じデータ テーブルに連結される場合など)、それらのコントロールはすべて同じ CurrencyManager を共有します。ただし、フォーム上のコントロールが異なるソースに連結される場合もあります。その場合は、フォーム上に複数の CurrencyManager オブジェクトが存在しており、各オブジェクトは、コントロールがどのレコードまたはデータ要素を使用しているかを追跡します。


 ◆上記内容について、以下の2つのデータ連結を例に CurrencyManager の共有について検証します。

  ・単純データ連結
    単純データ連結は、データソースのカレントレコードの 1 つのデータ要素にコントロールが連結されている
    例:TextBoxのText、ComboBoxのText・SelectedIndex・SelectedValue など
  ・複合データ連結
    複合データ連結は、データソースの複数のデータ要素にコントロールが連結されている
    例:DataGrid、ComboBox、ListBox などのDataSource に連結

【確認】
 ・単純データ連結
   Windows フォーム上に、TextBox と ComboBox を配置し、同じデータソースの何れかの列をバインドします。
   次にそれぞれのコントロールの BindingContext で CurrencyManager ( BindingManagerBase )を取得します。
   最後に、CurrencyManager ( BindingManagerBase )オブジェクトの比較を行うことで、同一と確認できます。

 ・複合データ連結
   Windows フォーム上に、DataGrid と ComboBox を配置し、同じデータソースを DataSource に設定します。
   次にそれぞれのコントロールの BindingContext で CurrencyManager ( BindingManagerBase )を取得します。
   最後に、CurrencyManager ( BindingManagerBase )オブジェクトの比較を行うことで、同一と確認できます。
   因みに、このことは ComboBox の DisplayMember に DataGrid の列の1つを設定することで確認できます。
   この時の挙動は、DataGrid のカレントレコードの内容が ComboBox に表示され、また ComboBox の選択値のレコードに
   DataGrid のカレントレコードが移動します。

 また、単純データ連結と複合データ連結のデータソースが同じ場合、単純データ連結/複合データ連結それぞれで取得できる  CurrencyManager ( BindingManagerBase )を比較すると、これも同一であると確認できます。

【結論】
 以上の結果より、Windows フォーム上の上記の CurrencyManager ( BindingManagerBase )オブジェクトは共有されていることが確認できました。

【注意】
 この検証から、Windows フォーム上のデータ連結について以下の注意が必要となります。
  ・複数の ComboBox/ListBox などのコントロールの選択項目用のデータソースに同じデータソースを連結することは避ける必要があります。
    何れかのコントロールで選択されると、BindingManagerBase のカレントレコード(Position)が移動し、別のコントロールがこれに連動します。
    これを回避するには、例えば、データソースの Clone() メソッド、または Copy() メソッドを利用して複製をつくる。
    または、 DataTable の場合は DataView を介することで、別々のデータソースの扱いとなり、
    異なる CurrencyManager ( BindingManagerBase )が生成され、回避できます。

【その他】
 CurrencyManager ( BindingManagerBase )が共通であることで、以下の利用法があります。
  ・上記検証例にあるように、DataGrid のカレントレコードを選択する方法として、ComboBox のリスト選択を利用することができます。
  ・引用の MSDN Library の使用例にあるカレントレコード移動機能、またはレコード検索機能などを、UserControl として生成し、
   データ連結機能を設けて、DataGrid の DataSource と同じデータソースに連結するだけで、
   カレントレコードの移動または検索機能が利用でき、汎用性の高いアプリケーションが構築できます。


【検証2】 カレントレコードの更新について



【MSDN Library の 「データ連結に関連するインターフェイス」の「IEditableObject インターフェイス」 引用】

IEditableObject インターフェイス
IEditableObject インターフェイスを実装するクラスを使用すると、オブジェクトへの変更を永続化するタイミングをオブジェクトが制御できるようになります。この実装により、BeginEdit、EndEdit、および CancelEdit の各メソッドが使用できるようになり、それによってオブジェクトへの変更をロールバックできます。

BeginEdit メソッド、EndEdit メソッド、および CancelEdit メソッドの機能と、データに加えられた変更のロールバックを可能にするためにこれらのメソッドをどのように組み合わせて使用するかについて、次に簡単に説明します。BeginEdit メソッド、EndEdit メソッド、および CancelEdit メソッドの動作の詳細については、「データセット更新の概要」を参照してください。

BeginEdit メソッド
オブジェクトの編集の開始を示します。このインターフェイスを実装するオブジェクトは、BeginEdit メソッドの呼び出し後に加えられたすべての更新を格納して、CancelEdit メソッドが呼び出されたときに破棄できるようにする必要があります。Windows フォームでデータ連結を行うときには、1 つの編集トランザクションのスコープ内でこのメソッドを複数回呼び出すことができます (たとえば、BeginEdit、BeginEdit、EndEdit)。IEditableObject インターフェイスの実装は、BeginEdit が既に呼び出されているかどうかを追跡して、以降の BeginEdit の呼び出しを無視する必要があります。このメソッドは複数回呼び出すことができるため、同じメソッドの以降の呼び出しが破壊的でないことが重要です。つまり、以降の BeginEdit の呼び出しでは、既に加えられている更新を破棄したり、最初の BeginEdit 呼び出し時に保存されたデータを変更したりすることはできません。

EndEdit メソッド
基になるオブジェクトが現在編集モードである場合に、BeginEdit の呼び出し後に加えられた変更をオブジェクトに反映します。

CancelEdit メソッド
オブジェクトに加えられた変更を破棄します。

データ機能のこの "トランザクション" という概念は、DataGrid コントロールで使用されています。


 ◆上記説明を基に、コントロールにデータ連結して利用する場合のカレントレコードの更新時における BeginEdit メソッド、EndEdit メソッド、および CancelEdit メソッドの呼び出しの必要性について検証します。

  ・BeginEdit メソッド
    MSDN Library には、「ユーザーがデータ連結コントロールの値を変更すると、 BeginEdit メソッドが暗黙的に呼び出されます。」
    と書かれており、データ連結を利用する場合、BeginEdit メソッドは特に注意する必要はない。
  ・EndEdit メソッド
    データ連結で利用する場合において、カレントレコードに加えられた変更をデータオブジェクトに反映するには、EndEdit メソッドを
    呼び出す必要があります。
  ・CancelEdit メソッド
    データ連結で利用する場合において、カレントレコードに加えられた変更を破棄するには、CancelEdit メソッドを呼び出す必要があります。


【確認】
 TextBox(単純データ連結型)/DataGrid(複合データ連結型)の2種類のコントロールそれぞれにデータ連結して、カレントレコードの変更を行い、各メソッドの必要性を確認します。 この時、変更手順として、以下の順序で操作します。
    コントロールの表示内容を変更(1) → カレントレコードを移動 → コントロールの表示内容を変更(2) → 別コントロールにフォーカスを移動
    → CancelEdit メソッドを呼び出す → カレントレコードを移動 → カレントレコードを戻す

 ・TextBox(単純データ連結型)の場合
   変更(1)はデータに反映されているが、変更(2)はデータに反映されない。

 ・DataGrid(複合データ連結型)の場合
   変更(1)・変更(2)共にデータに反映される。
   但し、フォーカスが移動しない状態では、TextBox と同様に変更が確定していないので注意する必要があります。

【結論】
 カレントレコードの更新時においては、BeginEdit メソッドは特に注意する必要はないが、TextBox等の単純データ連結の場合は、カレントレコードに加えられた変更が確定していない場合、EndEdit メソッドを呼び出す必要があります。


【検証3】 ユーザーコントロールのカスタムプロパティのデータ連結について



 Windows フォーム上のユーザーコントロールにカスタムプロパティを設定して、データソースに単純連結する場合、データ連結を管理する CurrencyManager ( BindingManagerBase )にデータの更新を通知する必要があります。
 MSDN Library によると、更新の通知をする為に、プロパティ変更イベントを発生させる必要があり、またこのイベントはプロパティ変更イベントの名前付け規則に従う必要があります。プロパティ名にサフィックス Changed を追加します。

 【MSDN Library の 「プロパティの使用方法のガイドライン」の「プロパティ変更イベントの発生」 引用】
 コンポーネントのプロパティがプログラムによって変更されたときに、コンポーネントのコンシューマに変更について通知するには、プロパティ変更イベントを発生させます。プロパティ変更イベントの名前付け規則に従い、プロパティ名にサフィックス Changed を追加して TextChanged のような名前にします。たとえば、Text プロパティが変更されたときに、コントロールは TextChanged イベントを発生させます。プロテクト ヘルパ ルーチン Raise<Property>Changed を使用して、このイベントを発生させることができます。しかし、ハッシュ テーブルの項目が追加されたときにプロパティ変更イベントを発生させても、そのオーバーヘッドに見合う効果は得られません。

 データ連結では、このパターンを使用して、プロパティの双方向の連結を実現しています。<Property>Changed イベントと Raise<Property>Changed イベントが発生しない場合には、データ連結は一方向にしか機能しませんが、データベースが変更されると、該当するプロパティも更新されます。この場合、<Property>Changed イベントを発生させる各プロパティが、それぞれがデータ連結をサポートしていることを示すメタデータを提供する必要があります。


【確認】
 以前、MSDN Library に解説されていることに気付いていないときに、カスタムプロパティのデータ連結について、試行錯誤した結果、以下のことを確認しています。

 ・プロパティ変更イベントを設けない場合
   データ連結は読み取り専用の一方通行は機能します。
   また、更新のデータ連結は、同じレコードで別のフィールドが更新された場合には、この項目の更新内容も反映されますが、
   この項目のみ更新された場合は、更新内容が反映されません。
   この現象で想定できるのは、該当レコードの何れか連結している項目のプロパティ変更イベントにより、 レコードオブジェクトのBeginEdit() メソッド
   が呼び出される。 これにより、レコードが編集状態になる。 また、編集状態移行後 EndEdit() メソッドにより編集が確定し、
   CurrencyManager の ItemChanged イベントが発生すると考えられます。
   上記で、単独で更新された場合は、BeginEdit() メソッドが呼び出されず、この編集状態に移行できなかったと考えられます。

 ・プロパティ変更イベントの名前付け規則に従い、プロパティ変更イベントを設けた場合、データ連結は読み書き双方向が機能します。

【補足】
戻る