印刷用ページ       送信     
クリックして評価とフィードバックをお寄せください
MSDN
MSDN ライブラリ
テクニカルドキュメント
コラム
Wonders of Windows Forms
 カスタム データ バインド (第 2 部)

  低帯域幅での表示をオンにする
カスタム データ バインド (第 2 部)

Michael Weinhardt
http://www.mikedub.net/ Leave-ms

February 6, 2005
日本語版最終更新日 : 2005 年 4 月 8 日

要約: 第 2 部でも、カスタム データ バインドの話を続けましょう。BindingSource で 1 つの型を 1 つのリスト データ ソースに変換できるようにする際に、BindingList<T> が果たす役割を簡単に見ていきます。また、並べ替えや検索など、BindingSource に依存するだけでは不十分な状況についても検討します。このような機能を使用するには、もう 1 歩踏み込んで他のサポートを追加し、独自の BindingList<T> を実装する必要があるので、これについても見ていきます。最終的には、ゲーム データのシリアル化を追加してさらに簡単にします。サンプル プログラム ファイル内では実際のコメント行は英語で書かれていますが、この記事内では説明目的で日本語で書かれています。この記事には英語のページへのリンクも含まれています。

注: この記事では、2004 年 12 月の CTP バージョンの .NET Framework 2.0 および Visual Studio .NET を対象にしています。

winforms02182005_sample.msi ファイルのダウンロード

前回の学習内容

第 1 部で作成した図 1 の Chinese Pronunciation Game Data アプリケーションを思い出してください。第 1 部の目的は、学習カード式ゲームで使用するデータをキャプチャすることでした。

ms993236.winforms02182005_fig1(ja-jp,MSDN.10).gif
図 1. Chinese Pronunciation Game Data 入力アプリケーション


前回はカスタムの Word 型を作成して任意のゲーム データをキャプチャすることから始めました。

public class Word {
  public Image Character { get; set; }
  public string English { get; set; }
  public string Pinyin { get; set; }
  public byte[] Pronunciation { get; set; }
}

"Windows Forms 2.0" のデータ デザイナのおかげで、1 行のデータ バインド コードを書かなくても、Word 型をリスト データ ソースに変換して DataGridView コントロールにバインドし、VCR スタイルのナビゲーションを追加できました。

注: 今にして思えば、12 月の Visual Studio .NET CTP の Class Designer 機能を使用すれば、まったくコーディングをすることなくビジュアルに Word 型を作成できました。

BindingSource では、すべてのカスタム型を編集できるリスト データ ソースを実現するために、System.ComponentModel.Collections 名前空間の BindingList<T> という小さいながらも非常に便利なテクノロジに依存しています。

BindingList<T>

BindingList<T> は IBindingList インターフェイス (System.ComponentModel 名前空間) のジェネリック実装です。また、リスト データ ソースで編集機能をサポートするために、データ バインド インフラストラクチャで最低限必要になるインターフェイスです。IList はバインド可能なリスト データ ソースの実装に必要な最低限のインターフェイスですが、このインターフェイスでは編集機能は提供されません。これは、ListBox などの編集できないコントロールにバインドされる場合は問題ありません。ただし、DataGridView など、編集機能を完全にサポートしているコントロールにバインドされる場合は、並べ替え、検索、インデックス作成、および変更通知などの機能のサポート以外に、リスト データ ソース内での編集機能が必須になります。IBindingList は IList から派生しているので、このような機能をすべてサポートするように拡張されます。

public interface IBindingList : IList, ICollection, IEnumerable {

  // メンバの編集
  bool AllowNew { get; } // 新しいリスト データ アイテムの追加をサポートするかどうか。
  bool AllowEdit { get; } // リスト データ アイテムの編集をサポートするかどうか。
  bool AllowRemove { get; } // リスト データ アイテムの削除をサポートするかどうか。
  object AddNew(); // 新しいリスト データを追加して返します。
  
  // メンバの並べ替え
  bool SupportsSorting { get; } // サポートされているかどうか。
  bool IsSorted { get; } // リスト データ ソースが並べ替えられているかどうか。
  PropertyDescriptor SortProperty { get; } // 現在の並べ替え列。
  ListSortDirection SortDirection { get; } // 現在の並べ替えの方向。
  void ApplySort(
    PropertyDescriptor property, 
    ListSortDirection direction); // リストを列と方向で並べ替えます。
  void RemoveSort(); // 並べ替えられていない状態に戻します。

  // メンバの検索
  bool SupportsSearching { get; } Supported?
  int Find(
    PropertyDescriptor property, 
    object key); // 指定されたキー値と一致する指定されたプロパティを持つ
                 // リスト データ アイテムを検索します。
  
  // メンバのインデックス作成
  void AddIndex(
    PropertyDescriptor property); // 目的の列にインデックスを追加します。
  void RemoveIndex(
    PropertyDescriptor property); // 目的の列からインデックスを削除します。

  // 変更通知メンバ
  bool SupportsChangeNotification { get; } // サポートされているかどうか。
  event ListChangedEventHandler ListChanged; // リストの変更をブロードキャストします。
}

BindingList<T> では、新しい .NET 2.0 ジェネリック サポートを使用して IBindingList を実装し、すべてのカスタム型を厳密に型指定された IBindingList リスト データ ソースにできるようにします。特に、バインド可能なプロパティについてリスト データ ソースを調べる場合、厳密な型指定には、データ バインド インフラストラクチャ関連の処理が簡単になるという独特のメリットがあります。

注: ジェネリックの説明はこの記事の範囲外なので取り上げていませんが、さらに詳しく調べることを強くお勧めします。詳細については、まず http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/csharp_generics.asp を参照してください。

BindingSource で使用されるコードはやや複雑になりますが、次の例では、BindingList<T> を実装した場合の Word 型で、どの程度コードが簡単になるかを示します。

BindingList<Word> words = new BindingList<Word>();

DataGridView にバインドすると、BindingList<T> 実装でリスト変更通知がサポートされるので、DataGridView は優れたデータ バインド インフラストラクチャ リスト データ ソースになります。たとえば、バインドされているクライアントにリストの変更をブロードキャストするために、よく知られているデータ バインド メカニズムを使用する場合などです。実際には、BindingList<T> では、データ バインド インフラストラクチャに統合するためにかなり多くのことが行われていますが、これを説明するの大変なことです。

BindingList<T>: 足りないもの

DataGridView では、ユーザーが行を追加、更新、削除できるビジュアルなメカニズムが提供されます。また、BindingList<T> リスト通知の優れたサポートにより、コントロールとリスト データ ソースとの間の同期が提供されます。BindingList<T> により IBindingList が実装されます。IBindingList は、実装が提供するサービスのレベルをデータ バインド インフラストラクチャに通知するインターフェイスです。ただし、必ずしも IBindingList でインターフェイス "全体" を実装する必要はないので、結局、並べ替え、検索、およびインデックス作成に関しては BindingList<T> からは通知されません。この理由の 1 つは、本当の意味でジェネリックな並べ替えや検索のアルゴリズムを実装することが非常に難しいからだと考えられます。特に、型が異なる場合はプロパティも異なる可能性があり、並べ替えや検索が可能かどうかがわかりません。どちらにしても、このことを検討しておくことは重要です。ここまで見てきたように、これにより、BindingSource を使用して自動的にリスト データ ソースを作成し管理できるかどうかが決まります。また、並べ替え、検索、インデックス作成コードのいずれか、あるいはこれら 3 つを組み合わせたコードを独自に記述する必要があるかどうかが決まります。これらの実装はいずれも DataGridView 自体に組み込むか、DataGridView を使用して作成できますが、このロジックをカスタムの IBindingList に組み入れることにより、リスト データ ソースに格納されているカスタム型の柔軟な対応力をさまざまな場所で何度も移植および再利用できるようになります。

Chinese Pronunciation Game Data マネージャなどのアプリケーションでは、おそらく何百行、何千行ものデータが表示されるので、並べ替え機能や検索機能があれば確実にメリットが得られるでしょう。この記事の残りの部分では、並べ替え可能な BindingList<T> 実装の作成に重点を置いて説明します。

並べ替え可能な BindingList<T> を作成する

並べ替えを実装するには、IBindingList とその並べ替えメンバを必要に応じて実装する必要があります。並べ替えメンバには以下のものがあります。

public interface IBindingList : IList, ICollection, IEnumerable {
  ...
  // 並べ替えメンバ
  bool SupportsSorting { get; }
  bool IsSorted { get; }
  PropertyDescriptor SortProperty { get; }
  ListSortDirection SortDirection { get; }
  void ApplySort(
    PropertyDescriptor property, 
    ListSortDirection direction);
  void RemoveSort();
  ...
}

BindingList<T> からは並べ替え機能は提供されませんが、必要な基本的なリスト データ ソースがサポートされます。また、完全な IBindingList 実装を最初から作成するのではなく BindingList<T> を出発点にして作成します。BindingList<Word> のように型を派生できますが、この場合は Word 型固有になります。代わりに、次に示すように少し手を加えて、ジェネリックな並べ替え可能なバインド リストを作成できます。

public class SortableBindingList<T> : BindingList<T> {}

このようにすると、Word 型を含めたあらゆる型に並べ替え機能を提供できるクラスになります。BindingList<T> からは並べ替え機能は提供されませんが、実際には、独自の並べ替え機能を簡単に統合するために必要なフックが提供されます。

中核となるメンバ

これらのフックは、オーバーライドする一連のプロパティとメソッドで構成されます。これらのフックはすべて、実装する IBindingList メンバの名前に "Core" サフィックスを付加した名前が付けられます。中核となる一連の並べ替え用メンバ全体を次に示します。

public class BindingList<T> : IBindingList, ... {
  // 中核となる並べ替えメンバ
  protected virtual void ApplySortCore(PropertyDescriptor property, ListSortDirection direction);
  protected virtual void RemoveSortCore();

  // 中核となる並べ替えプロパティ
  protected virtual bool SupportsSortingCore { get; }
  protected virtual bool IsSortedCore { get; }
  protected virtual ListSortDirection SortDirectionCore { get; }
  protected virtual PropertyDescriptor SortPropertyCore { get; }
}

BindingList<T> 実装では、中核となるメンバに対応する各 IBindingList メンバは、中核となるメンバのラッパーにすぎません。たとえば、次に示すように SupportsSorting プロパティでは SupportsSortingCore メソッドを呼び出して実際の処理を行います。


public class BindingList<T> : IBindingList, ... {
  public bool SupportsSorting {
    get {return this.SupportsSortingCore; }
  }
  protected virtual bool SupportsSortingCore {
    get { return false; }

  }
}


お分かりのように、BindingList<T> の SupportsSortingCore プロパティからは false が返されます。これは、データ バインド環境では並べ替えがサポートされないことを示します。完全な並べ替えソリューションを実装するには、IsSortedCore、SortPropertyCore、SortDirectionCore、ApplySortCore、RemoveSortCore、SupportsSortingCore など、適切な並べ替えメソッドをそれぞれオーバーライドする必要があります。

並べ替えをサポートするかどうか

ここまでの説明で既にお分かりでしょうが、カスタム リスト データ ソースでは、SupportsSortingCore をオーバーライドして true を返すことにより、並べ替えをサポートすることをブロードキャストする必要があります。これは次のように行います。

public class SortableBindingList<T> : BindingList<T> {}
  protected virtual bool SupportsSortingCore {
    get { return true; }
  }
}

簡単でしょう。もちろん、これで説明はすべて終わりです。では、実際に並べ替えアルゴリズムを実装しましょう。私が "アルゴリズム" という言葉を耳にすると必ず現実逃避になり、代わりに大好きなゲームのことで頭をいっぱいにします。読者のみなさんが私と同じなら、このような状況では好きなことを行ってみてください。そして、結果をできるだけすぐに教えてください。

並べ替えアルゴリズムを適用する

並べ替えを実装するには ApplySortCore をオーバーライドする必要があります。ApplySortCore は並べ替えを専門に処理します。特に、DataGridView など、バインドされるコントロールよってプロセスが実行されている間に、並べ替えアルゴリズムだけを実行するのが ApplySortCore の役割です。DataGridView での並べ替え処理は、列ヘッダーをマウス左ボタンでクリックすると開始されます。この並べ替え処理は 3 段階のプロセスで構成されます。第 1 段階では、必要な並べ替えメタデータを計算します。並べ替えメタデータは並べ替える列と順序であり、それぞれ PropertyDescriptor 列挙値と ListSortDirection 列挙値に変換されます。第 2 段階では DataGridView に注目します。ここでは、必要な PropertyDescriptor 引数と ListSortDirection 引数を渡して、IBindingList.ApplySort を呼び出し、データ ソース自体を並べ替えるようにデータソースに指示します。ApplySort から戻るときに、新しい並べ替え順序で DataGridView のデータが再表示されます。カスタム バインド リストで行うべき処理は、ApplySort に渡された並べ替え引数を受け取り、自身を並べ替えることだけです。

実際には BindingList<T> は Collection<T> から派生し、1 つ以上のリスト データ アイテムを管理するためのメカニズムを提供します。Collection<T> は、Items プロパティを公開します。このプロパティは、並べ替えの実行対象として必要な List<T> への参照を返します。好都合なことに、List<T> ではこの目的のための Sort メソッドを 公開します。このメソッドの使用は IComparer 実装をどのように利用するかに依存しています。IComparer は、リスト データでの並べ替えを行う際に必要なアイテムごとの比較を行います。IComparer を実装する種類により 2 つの値を受け取る方法が決まります。IComparer は、最初の引数値の方が大きい (1)、最初の引数値の方が小さい (-1)、または両方の引数値が等しい (0) ことを示す整数値を返します。Sort にはいくつかのオーバーロードがあります。以下に示すように、それぞれのオーバーロードは、使用する IComparer を渡すさまざまな方法を提供します。

public class List<T> : IList<T>, ... {
  ...

  // システム提供の IComparer を使用します。

  public void Sort();

  // カスタム IComparer<T> オブジェクトを使用します。

  public void Sort(IComparer<T> comparer);

  // Comparison<T> デリゲートを呼び出して並べ替えを実行する 


  // IComparer shim を作成します。

  public void Sort(Comparison<T> comparison);

  // カスタム IComparer<T> オブジェクトを使用してリストの一部を並べ替えます。

  public void Sort(int index, int count, IComparer<T> comparer);
  ...
}

この記事では、IComparer<T> から派生するカスタム PropertyComparer<T> クラスを次のように実装します。

public class PropertyComparer<T> :

System.Collections.Generic.IComparer<T> {

// コンストラクタ

  public PropertyComparer(
    PropertyDescriptor property, ListSortDirection direction) {...}
  // IComparer<T> インターフェイス

public int Compare(T xValue, T yValue) {...}

  public bool Equals(T xValue, T yValue) {...}
  public int GetHashCode(T obj) {...}
  ...
}

PropertyComparer は Rockford Lhotka によって作成された基本の比較アルゴリズムに基づいており、すべての型に使用できるジェネリック プロパティ比較子になります。

注: 比較子の詳細な分析はこの記事で扱う範囲を超えており、簡単な説明で十分と考えます。ただし、Rocky のすばらしい記事を読み、コード サンプルを調査することを強くお勧めします。

PropertyComparer のコンストラクタは、2 つの引数として、列挙値 PropertyDescriptor と ListSortDirection を受け取ります。これらの引数は比較時に使用されます。つまり、Compare メソッドが呼び出されるときに使用されます。Compare 自体は 2 種類のインスタンスを受け取り、両方のインスタンスのコンストラクタのプロパティ引数で指定されるプロパティ値を取得し、それらを比較して結果を返します。

PropertyComparer インスタンスを作成して ApplySort 内にこれらの処理をまとめます。その際、コンストラクタに適切な引数を渡した後、PropertyComparer への参照を List<T>.Sort に渡していることを確認します。List<T>.Sort では、指定されたプロパティを使用して希望する順序でリストを並べ替える際に必要な回数だけ、PropertyComparer の Compare メソッドを呼び出します。

public class SortableBindingList<T> : BindingList<T> {
  ...
  protected override void ApplySortCore(
    PropertyDescriptor property, ListSortDirection direction) {
    // 並べ替えるリストを取得します。
    List<T> items = this.Items as List<T>;

    // 並べ替えるアイテムがある場合は、sort を適用して設定します。
    if( items != null ) {
      PropertyComparer<T> pc = 
        new PropertyComparer<T>(property, direction);
      items.Sort(pc);
    }

    // バインドされたコントロールに表示を更新するように指示します。
    this.OnListChanged(
      new ListChangedEventArgs(ListChangedType.Reset, -1));
  }
}

ListChange をブロードキャストする

並べ替えの終了後、バインドされたすべてのコントロールに、リスト データ ソースの順序が変更されたため新しい順序で再表示すべきことを通知する必要があります。上記のコード サンプルのように、バインドされたコントロールに通知するには BindingList<T> から継承した OnListChanged メソッドを呼び出し、適切な ListChangedType 列挙値を渡す必要があります。

this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));

値 Reset は、リスト全体が変更された可能性があることを意味します。こうすることで、並べ替え操作後のつじつまを合わせます。ListChangedType (名前空間は System.ComponentMode) には、次に示すように他にも値がいくつかあります。

public enum ListChangedType {
  ItemAdded = 1, // リスト アイテムが追加されました。
  ItemChanged = 4, // リスト アイテムが変更されました。
  ItemDeleted = 2, // リスト アイテムが削除されました。
  ItemMoved = 3, // リスト アイテムがリスト内の新しい位置に移動されました。
  PropertyDescriptorAdded = 5, // リスト スキーマが変更されました (新しいプロパティ)。
  PropertyDescriptorChanged = 7, // リスト スキーマが変更されました (プロパティが変更されました)。
  PropertyDescriptorDeleted = 6, // リスト スキーマが変更されました (プロパティが削除されました)。
  Reset = 0 // リスト全体の変更です。
}

Collection<T> や List<T> を含め、BindingList<T> の内部が並べ替えに関して、すべてジェネリックになっていることにお気づきでしょう。つまり、並べ替えインフラストラクチャ全体にわたって厳密な型指定が行われ、パフォーマンスの点でも優れたものになっています。

現在の並べ替えの状態

並べ替えた後は、IBindingList 実装で現在の並べ替えについてレポートする必要があります。このレポートを行うために IsSortedCore プロパティをオーバーライドする必要があります。その結果、データ バインド インフラストラクチャは必要に応じてその実装の並べ替え状態を調査できるようになります。これは、次のようにプライベート状態変数により簡単に実現できます。

public class SortableBindingList<T> : BindingList<T> {

  private bool _isSorted;


  protected override void ApplySortCore(
    PropertyDescriptor property, ListSortDirection direction) {

    // 並べ替えるリストを取得します。
    List<T> items = this.Items as List<T>;

    // 並べ替えるアイテムがある場合は、sort を適用して設定します。
    if( items != null ) {
      PropertyComparer<T> pc = new PropertyComparer<T>(property, direction);
      items.Sort(pc);

      isSorted = true;

    }
    else {

      isSorted = false;

    }

    // バインドされたコントロールに表示を更新するように指示します。
    this.OnListChanged(
      new ListChangedEventArgs(ListChangedType.Reset, -1));
  }


  protected override bool IsSortedCore {


    get { return _isSorted; }


  }

}

サンプルに示すように、実装後、並べ替え時に ApplySortCore を更新してその実装の並べ替え状態を true に変更する必要があります。

並べ替えの取り消し

同様に、並べ替えを取り消すときは、最低でも並べ替え状態を false に設定する必要があります。次に示すように、このロジックは RemoveSortCore メソッドに実装する必要があります。

public class SortableBindingList<T> : BindingList<T> {

  private bool _isSorted;

  ...

  protected override void RemoveSortCore() {


    _isSorted = false;


  }

  ...
}

RemoveSort を実際に使用する場合は、基礎となるリスト データ ソースを直接並べ替えるのではなく、専用の並べ替え済みビューを使用する必要があります。これは ADO.NET で使用される DataView と DataTable のモデルによく似ています。繰り返しになりますが、詳細については Rocky の記事を参照することを強くお勧めします。

中核となる必要な並べ替えメソッドをオーバーライドして実装したら、並べ替え可能なカスタム リスト データ ソースが完成します。これを DataGridView にバインドする必要があります。

SortableBindingList<T> を使用する

現時点では、BindingSource コンポーネントにバインドされる DataGridView のようなフォームでは、フォーム自体が Word 型にバインドされています。前回の記事での作業の結果、DataGridView は実行時に適切な列を表示するように構成されています。これは、特にバインドされた BindingSource によって行われています。ここで行う必要のあることは、次のように、Word 型の BindingSource のデータ ソースを SortableBindingList<T> に代入することだけです。

partial class MainForm : Form {
  private void MainForm_Load(object sender, EventArgs e) {
    ...

    SortableBindingList<Word> source = new SortableBindingList<Word>();


    this.wordBindingSource.DataSource = source;

  }
  ...
}

ここでこのアプリケーションを実行すると、図 2 に示すように、DataGridView で並べ替えを実行できるようになります。

ms993236.winforms02182005_fig2(ja-jp,MSDN.10).gif
図 2. 並べ替え可能な DataGridView


DataGridView は、並べ替えの結果以外に、想定どおりの逆三角形の並べ替えマークを表示します。

保存する

データ管理アプリケーションでは、並べ替え機能があると便利ですが、実際にデータを読み込んで保存できるとさらに便利です。保存は、第 1 部で私が取り上げなかった機能ですが、プロジェクトの bin\debug フォルダに用意されているファイル gamedata.dat を使用する基本的な Save/Load 実装をここで組み込みました。

注: ドキュメント中心のアプリケーションが示す動作を詳しく説明している記事については、Chris Sells の「Windows フォームでドキュメント中心のアプリケーションを作成する」シリーズを参照してください。

.NET Framework 2.0 と BindingList<T> の両方に関して興味深い点は、Word データのシリアル化とシリアル化解除の実際の動作がどれほど簡単かということです。BindingList<T> はシリアル化可能ですが、ここではデータ アイテムの保存と読み込みに関心があります。これは、BindingList<T> の Items プロパティが同様にシリアル化できることにより可能になります。その結果、データ アイテムのリスト全体を 1 回で保存し読み込むことができます。ここでは SortableBindingList に Load メソッドと Save メソッドを実装して他でも使用できるようにしました。

public class SortableBindingList<T> : BindingList<T> {
  // 注: BindingList<T> はシリアル化可能ではありませんが List<T> はシリアル化可能です。

  public void Save(string filename) {
    BinaryFormatter formatter = new BinaryFormatter();
    using( FileStream stream = 
      new FileStream(filename, FileMode.Create) ) {
      // データ リスト アイテムをシリアル化します。
      formatter.Serialize(stream, (List<T>)this.Items);
    }
  }

  public void Load(string filename) {
    this.ClearItems();
    if( File.Exists(filename) ) {
      BinaryFormatter formatter = new BinaryFormatter();
      using( FileStream stream = 
        new FileStream(filename, FileMode.Open) ) {
        // データ リスト アイテムをシリアル化を解除します。
        ((List<T>)this.Items).AddRange(
          (IEnumerable<T>)formatter.Deserialize(stream));
      }
    }

    // バインドされたコントロールに表示を更新するように指示します。
    this.OnListChanged(
      new ListChangedEventArgs(ListChangedType.Reset, -1));
  }
}

シリアル化は簡単ですが、シリアル化解除 (Load) にはもう少し処理が必要です。簡単に言えば、SortableBindingList の List<T> インスタンスを読み込む最も高速な方法は AddRange メソッドを呼び出すことです。ただし、BinaryFormatter の Deserialize メソッドによって返される IEnumerable<T> 参照が必要になります。繰り返しになりますが、リスト データ ソースが消去され再度読み込まれるのに応じて、リスト全体がリセットされたことを通知するために OnListChanged が呼び出されます。

シリアル化ロジックは、シリアル化できる型だけで機能します。一般的に、シリアル化できる型は SerializableAttribute で修飾されているか、ISerializable を実装しているかのいずれかです。前者はプロパティ値だけをシリアル化する必要があるときに役立ち、後者はより複雑なシリアル化を実装できます。Word ではプロパティ値だけをシリアル化すればよいので、次のように、SerializableAttribute を使用して書き直しました。



[Serializable]

public class Word { ... }

一方 SortableBindingList<Word> インスタンスでは、以下に示すように、このインスタンスを単純に参照して Load および Save の両方を呼び出す必要があります。

partial class MainForm : Form {
  ...
  private void MainForm_Load(object sender, EventArgs e) {
    ...
    // ゲーム データ ファイルを開きます。
    SortableBindingList<Word> source = new SortableBindingList<Word>();
    source.Load(_filename);
    this.wordBindingSource.DataSource = source;
  }

  private void MainForm_FormClosing(
    object sender, FormClosingEventArgs e) {
    SortableBindingList<Word> list = 
      this.wordBindingSource.List as SortableBindingList<Word>;
    if( list != null ) {
      list.Save(_filename);
    }
  }
  ...
}

これで、Word ベースのリスト データ ソースが DataGridView に読み込まれ、DataGridView から保存されます。DataGridView 自体は適切に更新されます。

今回の学習内容

BindingSource がどのようにジェネリックな BindingList<T> 型に依存して任意の型を IBindingList 実装に変換しているのかを見てきました。しかし、その実装では並べ替え、検索、およびインデックス作成が実装されません。ただし、これを行うために必要なフックは、一連の中核となるメンバをオーバーライドすることにより提供されます。今回は並べ替えについてだけを説明し、基本的な保存については最後に少し触れました。

検索機能も実装するつもりでしたが、今月分のスペースが足りなくなったので取り上げていません。しかし、次回は、基本的な検索機能の実装を作成する方法を説明するだけではなく、UI に簡単に配置し、検索プロセス全体を簡単にできるカスタム Find ストリップ コントロールの作成方法も説明します。

謝辞

見放すことなく解説してくれた Steve Lasker と Joe Stegman に感謝します。特に、Joe は時間を割いて厄介なベータの問題に関して手伝ってくれ、マイナスをプラスへと巧みに変えてくれました。彼がいなければ、SortableBindingList<T> も PropertyComparer<T> も <T> にはならなかったでしょう。また、役立つものにもならなかったでしょう。

参考資料


Michael Weinhardt は、現在 .NET に関するさまざまな執筆にフルタイムで従事しています。著作には、Chris Sells との共著『Windows Forms Programming in C#, 2nd Edition』(Addison Wesley 刊) やこのコラムの執筆があります。Michael のお気に入りは、.NET 全般、特に Windows フォームです。また、現代の歴史の中で最も進歩的な時代であると彼が考える、80 年代の音楽も崇拝しています。詳細については、http://www.mikedub.net/ Leave-ms をご覧ください。

© 2009 Microsoft Corporation. All rights reserved. 使用条件  |  商標  |  プライバシー
Page view tracker