DataGridView 列ヘッダー セルのDrop-Down フィルター リストの作成

 

カール・エリクソン
Microsoft Corporation

2006 年 7 月
更新日: 2007 年 12 月

適用対象:
   Microsoft® Visual Studio® 2005
   Microsoft® .NET Framework 2.0
   Microsoftâ Windowsâ Forms 2.0

 

概要:Microsoft Visual Studio® 2005 の Microsoft® Windows フォーム DataGridView コントロールは、Microsoft®® Excel と同様のグリッド エクスペリエンスを提供しますが、Excel がオートフィルター機能で提供する列フィルタードロップダウン リストは提供しません。 ただし、 DataGridView は、ADO.NET DataView などのフィルター処理を提供するデータ ソースにバインドできます。 この記事では、データ ソースと新しい BindingSource コンポーネントのフィルター機能を利用して、ドロップダウン フィルター リストを表示するカスタム DataGridView 列ヘッダー セルを作成する方法について説明します。

Microsoft ダウンロード センターで C# と Visual Basic (128 KB) のコード サンプルをダウンロードします。

内容

     列フィルター処理の概要
     列のフィルター処理と DataGridView コントロール
     DataGridViewAutoFilter クラス ライブラリ
     DataGridViewAutoFilter ライブラリの使用
     DataGridViewAutoFilterColumnHeaderCell クラスの実装の詳細
     DataGridViewAutoFilterTextBoxColumn クラスの実装の詳細
     考えられる機能強化
     その他のリソース

列フィルター処理の概要

DataGridView コントロールは、複数行の表形式データを表示するための優れたツールです。 ただし、 DataGridView コントロールに大量のデータを一度に表示すると、ユーザーが必要な情報を見つけるのが困難になる可能性があります。 この問題に対処する 1 つの方法は、ユーザーが特定の列で並べ替えられるように、自動並べ替えを有効にすることです。 もう 1 つの方法は、ユーザーが特定の列に特定の値を持つ行のみを表示できるように、列フィルター処理を実装することです。

DataGridView コントロールは、データ ソースが並べ替えをサポートしていると仮定して、ユーザーが列ヘッダーをクリックできるようにすることで、自動並べ替えを既に提供しています。 ただし、フィルター処理のサポートは組み込まれています。 フィルター処理をサポートするデータ ソースを使用できますが、ユーザーがフィルター処理に影響を与えるようにするには、独自のユーザー インターフェイス (UI) を指定する必要があります。

一部のデータ ソースのみがフィルター処理をサポートしています。 一般的な方法の 1 つは、DataGridView コントロールを DataSet にバインドされた BindingSource コンポーネントにバインドすることです。 これは、Visual Studio 2005 Windows フォーム Designerを使用してデータ バインディングを設定するときに作成される構成です。 その後、ユーザー入力に応じて BindingSource.Filter プロパティを設定できます。

フォームにスペースがある場合は、テキスト ボックスとコンボ ボックスを使用して列フィルター オプションを設定するためのユーザー インターフェイスを提供できます。 ただし、スペースが限られている場合や、アプリケーションで列フィルター処理が一般的なタスクである場合は、Excel AutoFilter 機能のように、列ヘッダーに列フィルタードロップダウン リストを直接指定できます。 これは、子フォームでドックで塗りつぶされた DataGridView を表示する場合に特に便利です。

Note

注: この記事の残りの部分では、Excel の "オートフィルター" という用語を使用して、Excel スタイルの列フィルタードロップダウン リストを参照します。

列のフィルター処理と DataGridView コントロール

DataGridView コントロールにオートフィルター機能が含まれていない理由を疑問に思うかもしれません。 DataGridView の設計目標は、最も一般的なすべての機能を備えた柔軟なグリッド コントロールを作成することでした。一方で、ほぼすべてのユーザーが共通の機能では満足できない特別なニーズを持つことになります。 コントロールは基本をカバーする必要がありましたが、外観と動作の両方で簡単にカスタマイズでき、置き換える DataGrid コントロールを大幅に改善する必要がありました。 オートフィルター機能は、フィルター処理を管理するためにフォーム上の他の場所で UI を提供できるため、必須ではありません。 オートフィルター機能は自分で実装することもできますが、これには高度なカスタマイズが含まれます。

DataGridView コントロールは、いくつかの異なるレベルでカスタマイズできます。 たとえば、クライアント アプリケーションでスタイル プロパティを設定し、ペイント イベントを処理したり、既存の機能を利用するために既存の列とセルの種類を拡張したりして、カスタムの列とセルの種類を提供したりできます。 たとえば、DataGridViewTextBoxCell クラスを拡張する場合、任意のWindows フォーム コントロールをホストする新しい編集セルの種類を作成することは比較的簡単です。

オートフィルター機能を実装するには、編集コントロール機能を提供しない DataGridViewColumnHeaderCell クラスから派生する必要があります。 ドロップダウン リストを表示するように拡張することは、 DataGridViewComboBoxCell クラスを調整するよりも困難です。 代わりに、ドロップダウン ボタンの描画、マウスクリックのヒット テスト、ドロップダウン リストの配置と表示、ユーザー選択への適切な応答のすべての作業を行う必要があります。

DataGridViewAutoFilter クラス ライブラリ

この記事に付属するサンプルでは、AutoFilter 機能をライブラリの形式で実装するデモを DataGridViewAutoFilter 示します。 このライブラリには、 DataGridViewAutoFilterColumnHeaderCell クラスとサポート列クラスが含まれています。 このサンプルは、フル機能、汎用、バグフリー、Microsoft がサポートするソリューションではありませんが、独自の実装をガイドするために変更または使用できる基礎を提供します。

図 1 は、動作中のライブラリを DataGridViewAutoFilter 示しています。

図 1.DataGridViewAutoFilter

 

図 1. DataGridViewAutoFilter ライブラリを使用して実装されるオートフィルター機能。

サンプルの機能と依存関係については、次のセクションで詳しく説明します。これは、すぐに使用できる内容と、自分で提供する必要がある内容を理解するのに役立ちます。 これらは、機能と制限事項の完全な一覧ではありませんが、さらに調査するか、別のソリューションを探すかを決定するのに役立つ場合があります。 運用アプリケーションでサンプル コードを使用する前に、サポートするすべてのシナリオを十分にテストしてください。

特徴

サンプル DataGridViewAutoFilter ライブラリには、次の機能があります。

  • 複数列フィルター処理のサポート。 各ドロップダウン リストには、列内のすべての一意の値が表示され、他の列のフィルターが考慮されます。 列がフィルター処理されている場合、そのドロップダウン リストには、フィルター処理されていない列に表示される値が表示され、現在のフィルター値が選択されます。
  • 自動並べ替えのサポート。 ユーザーはヘッダー セルをクリックして列を並べ替えることができます。ドロップダウン ボタンの横に並べ替えグリフが表示されます。
  • 次の特殊なフィルター オプションのサポート: (すべて)、(空白)、および (非ブランク)。
  • Visual Studio 2005 のデザイン時にオートフィルター機能をアプリケーションに追加するためのサポート。

実装されていない機能

サンプル DataGridViewAutoFilter ライブラリには、Excel の使用が期待できる機能や、特定のアプリケーションに必要な機能が用意されていません。 実装されていない機能は次のとおりです。

  • ドロップダウン リストの並べ替えオプション。 自動並べ替えがサポートされているため、この Excel 機能は不要です。
  • (Custom...) や (上位 10....) などの追加の特別なフィルター オプション。これらのオプションは、このサンプルの範囲外です。 すべてのフィルター処理は、特定の値またはサポートされている特殊なフィルター オプションの 1 つを対象とします。 複雑なフィルターオプションを指定する必要がある場合は、Excel の場合と同様に、独自の [カスタム オートフィルター] ダイアログ ボックスを実装できます。
  • 非連結モードまたは仮想モードでのフィルター処理、またはフィルター処理を提供しないデータ ソースを使用したバインド モードでのフィルター処理はサポートされていません。 この機能は、このサンプルの範囲外です。 フィルター処理を提供する外部データ ソースを使用できない場合は、自分でフィルター処理を実装する必要があります。
  • 任意のデータ書式設定はサポートされません。 ドロップダウン リストに表示される文字列は、列のセル スタイルを使用して DataGridViewCell.GetFormattedValue プロパティを使用して取得されます。 DataGridViewCellStyle.Format プロパティを使用して、日付または通貨の書式を指定できます。 ただし、特別な書式設定のニーズがある場合は、それらを自分で実装する必要があります。

依存関係

サンプル DataGridViewAutoFilter ライブラリは、次の 4 つのことに依存します。

  • DataGridView.DataSourceBindingSource コンポーネントに設定する必要があります。
  • BindingSource コンポーネントは、IBindingListView 実装にバインドする必要があります。
  • IBindingListView.SupportsFiltering プロパティの値は true である必要があります。
  • IBindingListView.Filter プロパティは、複数列のフィルター処理をサポートしている必要があります。

これらの 4 つの要件は、Visual Studio 2005 Windows フォーム Designerを使用して、ADO.NET を使用してデータ バインディングを設定するときに自動的に満たされます。 (詳細については、「方法: MSDN2 ライブラリDesignerを使用してデータを Windows フォーム DataGridView コントロールにバインドする」を参照してください)。または、要件を満たす任意の IBindingListView 実装にバインドすることもできます。

既存のデータ ソースにバインドする必要がある場合は、データ ソースを BindingSource コンポーネントにバインドし、 BindingSourceDataGridView コントロールにバインドする必要があります。 BindingSource.RaiseListChangedEvents プロパティが原因で、BindingSource コンポーネントへの依存関係が必要です。 このプロパティを false に設定すると、 DataGridView コントロールに表示されるデータを更新せずにフィルターを一時的に変更できます。 これは、列自体がフィルター処理されている場合でも、ドロップダウン リストに表示される値を計算するために必要です。 代替データ ソースで同様の機能を提供できる場合は、この依存関係を回避できます。

フィルター処理をサポートするには、SupportsFiltering プロパティ値が trueIBindingListViewBindingSource をバインドする必要があります。 これは、 DataSet などの ADO.NET オブジェクトにバインドする場合です (たとえば、デザイナーを使用してデータ バインディングを設定する場合)。 DataSet クラスは IBindingListView 自体を実装していませんが、BindingSourceDataSet 内の適切な DataTable から DataView を取得できます。 この場合、 BindingSource.Filter プロパティは DataView.RowFilter プロパティにマップされ、 DataColumn.Expression プロパティを使用します。

IBindingListView インターフェイスでは、Filter プロパティの実装方法は指定されませんが、Filter プロパティを使用するアプリケーションでは、フィルター文字列の形式に関する特定の前提条件を満たす必要があります。 サンプル DataGridViewAutoFilter ライブラリでは、IBindingListView.Filter プロパティが、.NET Frameworkマネージド リファレンス ドキュメントの DataColumn.Expression トピックに記載されている構文を使用して、ブール式のフィルター文字列を受け入れることを想定しています。

DataGridViewAutoFilter ライブラリの使用

ライブラリはDataGridViewAutoFilter、 クラスと DataGridViewAutoFilterTextBoxColumn クラスにアクセスDataGridViewAutoFilterColumnHeaderCellするためにプロジェクトで参照できる 1 つのアセンブリです。 これらのクラスは、プログラムまたは Visual Studio 2005 のWindows フォーム Designerを使用して使用できます。

ヘッダー セルに [オートフィルター] ドロップダウン ボタンを表示するには、列の HeaderCell プロパティを クラスの DataGridViewAutoFilterColumnHeaderCell インスタンスに設定する必要があります。 特定の列の HeaderCell プロパティは、プログラムで設定できます。 ただし、Windows フォーム Designerでは、プロパティ ウィンドウで HeaderCell プロパティを設定することはできません。

デザイナー エクスペリエンスをサポートするために、 DataGridViewAutoFilterTextBoxColumn が提供されます。 デザイナーでは、[列の 編集] ダイアログ ボックスと [列の追加] ダイアログ ボックスでこの列の種類を選択できます。 はDataGridViewAutoFilterTextBoxColumnHeaderCell プロパティを クラスの新しいインスタンスに設定するために DataGridViewTextBoxColumnDataGridViewAutoFilterColumnHeaderCell拡張します。

アプリケーションに 1 つ以上の AutoFilter ヘッダー セルを追加したら、現在のフィルター状態に関するユーザー フィードバックを提供し、ユーザーにすべての行を表示する方法を提供できます。 BindingSource コンポーネントを使用すると、フィルター処理された行数を取得したり、フィルターを削除したりできますがDataGridViewAutoFilter、セルクラスと列クラスは便利なメソッドでこの機能を提供します。

フォーム コードからドロップダウン リストへのキーボード アクセスを提供することもできます。 Excel では、ユーザーはドロップダウン ボタンを含むセルに移動し、Alt キーを押しながら上方向キーまたは Alt キーを押しながら下方向キーを押すことができます。 ただし、 DataGridView コントロールでは、ユーザーはヘッダー セルに移動できません。 代わりに、Alt キーを押しながら上方向キーまたは下方向キーを押して、現在のセルを含む列のドロップダウン リストを表示することもできます。

次の手順では、クラスの 4 つの使用シナリオ DataGridViewAutoFilter について説明します。 最初の手順では、デザイナーを使用して、Windows フォーム アプリケーションにオートフィルター機能を追加する方法について説明します。 2 番目の手順では、プログラムで AutoFilter 機能を追加する方法について説明します。 3 番目の手順では、フィルター状態文字列と [すべて表示 ] オプションを表示して、クライアント アプリケーションを拡張する方法について説明します。 最後に、4 番目の手順では、ユーザーがキーボードを使用してドロップダウン リストを表示できるようにする方法について説明します。

デザイナーを使用してオートフィルター機能をアプリケーションに追加するには:

  1. Windows アプリケーション プロジェクトで、DataGridViewAutoFilter.dll アセンブリへの参照を追加します。
  2. DataGridView コントロールをフォームに追加し、コントロールのスマート タグの [データ ソースの選択] タスクを使用してデータをバインドします。 詳細については、MSDN2 ライブラリ「方法: Designerを使用してデータを Windows フォーム DataGridView コントロールにバインドする」を参照してください。
  3. 列が生成されたら、コントロールのスマート タグで [ 列の編集] をクリックします。
  4. [ 列の編集 ] ダイアログ ボックスで、列を選択します。
  5. ダイアログ ボックスの [プロパティ ] ウィンドウで、 ColumnType プロパティを選択し、ドロップダウン リストから選択 DataGridViewAutoFilterTextBoxColumn します。
  6. オートフィルター機能を使用するすべての列について、手順 4 と 5 を繰り返します。
  7. 列の構成が完了したら、アプリケーションを実行し、選択した列のヘッダーにドロップダウン ボタンが表示されることを確認します。

図 2 は、列の種類を に設定するために使用されている [列の 編集 ] ダイアログ ボックスのサンプルを DataGridViewAutoFilterTextBoxColumn示しています。

図 2.ColumnType プロパティが DataGridViewAutoFilterTextBoxColumn に設定されている [列の編集] ダイアログ ボックス。

図 2. ColumnType プロパティが DataGridViewAutoFilterTextBoxColumn に設定されている [列の編集] ダイアログ ボックス。

プログラムでオートフィルター機能をアプリケーションに追加するには:

  1. Windows アプリケーション プロジェクトで、DataGridViewAutoFilter.dll アセンブリへの参照を追加します。

  2. クラス名を修飾する必要がないように、次の using ステートメントをコード ファイルの先頭に DataGridViewAutoFilter 追加します。

  3. DataGridView コントロールをフォームに追加します。

  4. デザイナーまたは表示されるイベント フックアップ コードのいずれかを使用して、 DataGridView.BindingContextChanged イベントを処理します。 データ バインディングの結果としてイベントが発生するように、 DataSource プロパティを設定する前に、イベント ハンドラーを イベントに関連付ける必要があります。

  5. DataGridView.DataSource プロパティを設定します。 次のコードでは、デザイナーまたはコードで、 という名前customersBindingSourceBindingSource コンポーネントが既に作成および初期化されていることを前提としています。 プログラムによるデータ バインディングの設定の詳細については、MSDN2 ライブラリ「方法: データを Windows フォーム DataGridView コントロールにバインドする」を参照してください。

  6. BindingContextChanged イベント ハンドラーで、影響を与える列の DataGridViewColumn.HeaderCell プロパティを設定します。 次のコード例では、 DataGridView コントロールの列を反復処理し、各ヘッダーを新 DataGridViewAutoFilterColumnHeaderCell しいオブジェクトに設定します。

  7. アプリケーションを実行し、選択した列のヘッダーにドロップダウン ボタンが表示されることを確認します。

フィルターの状態と [すべて表示] オプションを表示する StatusStrip コントロールを追加するには:

  1. 前のいずれかの手順に従って、 DataGridView コントロールにオートフィルター機能を追加します。

  2. まだ行っていない場合は、次の using ステートメントをコード ファイルの先頭に追加して、クラス名を DataGrideViewAutoFilter 修飾する必要がないようにします。

        using DataGridViewAutoFilter;
    
  3. StatusStrip コントロールを という名前のフォームに追加し、 と showAllLabelという名前statusStrip1filterStatusLabelの 2 つの ToolStripStatusLabel コンポーネントを含みます。

  4. デザイナーの [プロパティ ] ウィンドウで、次のコードまたは同等の設定を使用してラベルを構成します。

      filterStatusLabel.Text = "";
      filterStatusLabel.Visible = false;
      showAllLabel.Text = "Show &All";
      showAllLabel.Visible = false;
      showAllLabel.IsLink = true;
      showAllLabel.LinkBehavior = LinkBehavior.HoverUnderline;
    
  5. コンポーネントの Click イベントを showAllLabel 処理します。

      // Add this code to the constructor or associate 
      // the handler with the event using the designer. 
      showAllLabel.Click += new EventHandler(showAllLabel_Click);
    
      // ...
    
      private void showAllLabel_Click(object sender, EventArgs e)
      {
          DataGridViewAutoFilterTextBoxColumn.RemoveFilter(dataGridView1);
      }
    
  6. DataGridView.DataBindingComplete イベントを処理します。

          // Add this code to the constructor or associate 
        // the handler with the event using the designer. 
        dataGridView1.DataBindingComplete += 
            new DataGridViewBindingCompleteEventHandler(
            dataGridView1_DataBindingComplete);
    
        // ...
    
        void dataGridView1_DataBindingComplete(object sender,
            DataGridViewBindingCompleteEventArgs e)
        {
            String filterStatus = DataGridViewAutoFilterColumnHeaderCell
                .GetFilterStatus(dataGridView1);
            if (String.IsNullOrEmpty(filterStatus))
            {
                showAllLabel.Visible = false;
                filterStatusLabel.Visible = false;
            }
            else
            {
                showAllLabel.Visible = true;
                filterStatusLabel.Visible = true;
                filterStatusLabel.Text = filterStatus;
    
        }
    
  7. アプリケーションを実行し、フィルターを適用します。

図 3 は、フィルターが適用された後の 結果の StatusStrip のサンプルを示しています。

図 3.フィルターの状態と [すべて表示] オプションを表示する StatusStrip コントロール。

図 3: フィルターの状態と [すべて表示] オプションを表示する StatusStrip コントロール。

ユーザーがキーボードを使用してドロップダウン リストを表示できるようにするには:

  1. まだ行っていない場合は、次の using ステートメントをコード ファイルの先頭に追加して、クラス名を DataGridViewAutoFilter 修飾する必要がないようにします。

      using DataGridViewAutoFilter;
    
  2. DataGridView.KeyDown イベントを処理します。

            // Add this code to the constructor.
        this.dataGridView1.KeyDown += new 
            KeyEventHandler(dataGridView1_KeyDown);
    
    
        // ...
    
        void dataGridView1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Alt && (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up))
            {
                DataGridViewAutoFilterColumnHeaderCell filterCell =
                    this.dataGridView1.CurrentCell.OwningColumn.HeaderCell as 
                    DataGridViewAutoFilterColumnHeaderCell;
                if (filterCell != null)
                {
                    filterCell.ShowDropDownList();
                    e.Handled = true;
                }
            }
        }
    
  3. アプリケーションを実行し、Alt キーを押しながら上方向キーまたは Alt キーを押しながら下方向キーを押して、現在の列のドロップダウン リストを開きます。 Esc キーを押してドロップダウン リストを閉じます。

DataGridViewAutoFilterColumnHeaderCell クラスの実装の詳細

ライブラリの使用方法 DataGridViewAutoFilter を確認し、その機能の一部を知ったので、この記事の残りの部分では、ライブラリのしくみ DataGridViewAutoFilter の詳細について説明します。

ライブラリは DataGridViewAutoFilter 、そのままの要件を満たしている可能性があり、実装の詳細を知る必要はありません。 ただし、ニーズに合わせて機能をカスタマイズまたは拡張したり、修正するバグを見つけたりする場合があります。 さらに、実装を理解することは、 DataGridView コントロールの他のカスタム ヘッダー セル型を作成する場合に役立ちます。

ライブラリの DataGridViewAutoFilter プライマリ クラスは クラスです DataGridViewAutoFilterColumnHeaderCell 。 このクラスは、 DataGridViewColumnHeaderCell クラスから派生します。

実装の詳細は DataGridViewAutoFilterColumnHeaderCell 、次の 4 つのカテゴリに分けることができます。

  • 初期化。
  • ドロップダウン ボタンを表示します。
  • ドロップダウン リストを使用したユーザー操作の表示、非表示、および処理。
  • ユーザーが一覧からフィルターを選択したときにバインドされたデータをフィルター処理する。

次のセクションでは、これらのカテゴリについて詳しく説明し、さまざまな点を示すコード例を示します。 コード例は付属 DataGridViewAutoFilter のライブラリから抜粋されていますが、簡潔にするためにいくつかの詳細は省略されています。 完全な詳細については、サンプルの完全なソース コードを参照してください。

初期化

プライマリ初期化タスクは、ドロップダウン リストとして使用する ListBox コントロールを作成することです。 ライブラリは DataGridViewAutoFilter 、アプリケーション内のすべての AutoFilter ヘッダー セルに対して単一の ListBox 派生コントロールを使用し、 という dropDownListBox静的変数に格納します。 一度に表示されるドロップダウン リストは 1 つだけであるため、1 つのインスタンスを使用できます。 また、ドロップダウン リストの内容、サイズ、場所は、フィルター処理、サイズ変更、スクロールなどの DataGridView コントロールへの頻繁な変更の影響を受けるので、リストが表示される時点で最も簡単に判断できるためです。

Windows フォーム ListBox コントロールは、Excel オートフィルター リストの外観と動作 (長いリストを移動するための垂直スクロール バーを含む) を提供するため使用されます。 これに対し、 ToolStripDropDown コントロールは垂直スクロール バーがないため使用されません。代わりに、メニューとしての一般的な使用方法に合わせて上下のボタンを使用します。 ComboBox コントロールは、コントロールのテキスト ボックス部分を表示しないとドロップダウン リストを簡単に表示できないため、使用されません。

FilterListBox クラス

コントロールは FilterListBoxListBox コントロールを拡張して、通常は親 DataGridView コントロールによってインターセプトされるキーボード メッセージを確実に受け取ります。 さらに、 クラスは FilterListBox 、ドロップダウン リストとして使用するコントロールを構成するために、いくつかの ListBox プロパティ設定を変更します。

クラスは FilterListBox 、ALT + F4 などのオペレーティング システムによって処理されるキーストロークを除くすべてのキーストロークをインターセプトするために、保護された IsInputKey メソッドと ProcessKeyMessage メソッドをオーバーライドします。 Control.ProcessKeyMessage メソッドは通常、コントロールの親の ProcessKeyPreview メソッドにメッセージをディスパッチします。 コントロールの親がメッセージに関心がない場合は、キーボード イベントを生成するために、コントロール独自の ProcessKeyEventArgs メソッドに移動します。

残念ながら、 DataGridView.ProcessKeyPreview メソッドは、編集モードの通常の (ヘッダー以外の) セルによってホストされる編集コントロール用でない限り、すべてのキーボード メッセージをインターセプトします。 (編集コントロールのホストの詳細については、「方法: MSDN2 ライブラリの DataGridView セルWindows フォームコントロールをホストする」を参照してください)。キーボード処理の場合、DataGridView コントロールは他のすべての子コントロールを無視します。 その結果、ホストされたコントロールが処理すると予想されるキーストロークは、代わりに DataGridView コントロールによって処理されます。

この動作を防ぐために、 クラスの FilterListBoxProcessKeyMessage オーバーライドは親コントロールの処理をスキップし、代わりにキーボード メッセージを ProcessKeyEventArgs メソッドに直接送信します。 その後、 ProcessKeyEventArgs メソッドは、クラスが FilterListBox 処理できるキーボード イベントを DataGridViewAutoFilterColumnHeaderCell 発生させます。

クラスのソース コードは FilterListBox 次のとおりです。


  private class FilterListBox : ListBox
  {
      public FilterListBox()
      {
          Visible = false;
          IntegralHeight = true;
          BorderStyle = BorderStyle.FixedSingle;
          TabStop = false;
      }

      protected override bool IsInputKey(Keys keyData)
      {
          return true;
      }

      protected override bool ProcessKeyMessage(ref Message m)
      {
          return ProcessKeyEventArgs(ref m);
      }
  }

コンストラクターと Clone メソッド

クラスは DataGridViewAutoFilterColumnHeaderCell 、空の既定のコンストラクターと、既存の DataGridViewColumnHeaderCell インスタンスを受け取るコンストラクター オーバーロードを提供します。 コンストラクター のオーバーロードは、指定したセルのプロパティ値を新しいインスタンスにコピーします。 これは、既存のヘッダー セルを AutoFilter セルに置き換え、他のすべてのヘッダーの詳細を同じままにする場合に便利です。

Clone メソッドは、コンストラクター オーバーロードを使用して、既存のインスタンスと同じプロパティ値を持つ新しいインスタンスを作成します。 このため、コンストラクターオーバーロードは、オートフィルター固有のプロパティ値をコピーできるように、指定された列ヘッダー セルも AutoFilter セルであるかどうかを判断します。

DataGridView コントロールは、DataSource が変更されたときに列とそのヘッダー セルを複製し、新しい列セットを自動的に生成する必要があります。 複製は、古いスキーマの列と一致する新しいスキーマ内のすべての列に使用されます。 こうすることで、複数のデータ ソースで同じ列を再構成する必要はありません。

このため、DataGridView セルまたは列の種類を拡張し、追加するパブリック プロパティ値をコピーするときは、Clone メソッドをオーバーライドすることが重要です。

既定以外のクラス コンストラクターと Clone メソッドのソース コードは次のとおりです。


  public DataGridViewAutoFilterColumnHeaderCell(DataGridViewColumnHeaderCell oldHeaderCell)
  {
      this.ContextMenuStrip = oldHeaderCell.ContextMenuStrip;
      this.ErrorText = oldHeaderCell.ErrorText;
      this.Tag = oldHeaderCell.Tag;
      this.ToolTipText = oldHeaderCell.ToolTipText;
      this.Value = oldHeaderCell.Value;
      this.ValueType = oldHeaderCell.ValueType;
      if (oldHeaderCell.HasStyle)
      {
          this.Style = oldHeaderCell.Style;
      }

      DataGridViewAutoFilterColumnHeaderCell filterCell =
          oldHeaderCell as DataGridViewAutoFilterColumnHeaderCell;
      if (filterCell != null)
      {
          this.FilteringEnabled = filterCell.FilteringEnabled;
          this.AutomaticSortingEnabled = filterCell.AutomaticSortingEnabled;
          this.DropDownListBoxMaxLines = filterCell.DropDownListBoxMaxLines;
          this.currentDropDownButtonPaddingOffset = 
              filterCell.currentDropDownButtonPaddingOffset;
      }
  }

  public override object Clone()
  {
      return new DataGridViewAutoFilterColumnHeaderCell(this);
  }

OnDataGridViewChanged メソッド

AutoFilter ヘッダー セルは、 作成時に DataGridView コントロールの一部ではなく、 DataGridView コントロールに含まれる列に追加された後に限られます。 この場合、 OnDataGridViewChanged メソッドが呼び出されます。 このクラスは DataGridViewAutoFilterColumnHeaderCellDataGridView コントロールを使用できるようになる前に実行できない初期化を提供するために、このメソッドをオーバーライドします。

AutoFilter ヘッダー セルは、それを含む DataGridView コントロールにアクセスすると、コントロールのデータ ソースにアクセスし、列の種類がフィルター処理と並べ替えに適していることを確認できます。 たとえば、列が Image 列の場合、セルの FilteringEnabled プロパティは false に設定され、ドロップダウン ボタンは表示されません。

OnDataGridViewChanged メソッドを使用すると、DataGridView コントロールが正しく構成されます。 最初に、コントロールの SortMode プロパティが Automatic に設定されていないことを確認 します。 通常、自動並べ替えは、ユーザーが列ヘッダー セルの任意の場所をクリックしたときに発生します。 ただし、オートフィルター セルの場合、ユーザーがドロップダウン ボタンで占有されているセルの部分をクリックしたときに、自動並べ替えを行う必要はありません。 このため、並べ替えは手動で実装する必要があり、 SortMode プロパティを Automatic に設定することはできません。

OnDataGridViewChanged メソッドは、 メソッドをVerifyDataSource呼び出して、DataGridView コントロールが有効なデータ ソースにバインドされているかどうかをチェックします。 この時点では、コントロールに有効な DataSource プロパティ値を指定する必要はありませんが、有効な場合、 VerifyDataSource メソッドは BindingSource であることを確認し、そうでない場合は例外をスローします。

OnDataGridViewChanged メソッドは、データ ソースの確認に加えて、ドロップダウン ボタンとリストの表示やデータ ソースの状態に影響を与えるさまざまな DataGridView イベントにイベント ハンドラーを関連付けます。 また、このメソッドは、最初の列の自動サイズ設定がボタンの幅に対応するように、ドロップダウン ボタンの境界を初期化します。

OnDataGridViewChanged メソッドのソース コードは次のとおりです。


  protected override void OnDataGridViewChanged()
  {
      if (this.DataGridView == null) return;

      if (OwningColumn != null)
      {
          if (OwningColumn is DataGridViewImageColumn ||
          (OwningColumn is DataGridViewButtonColumn && 
          ((DataGridViewButtonColumn)OwningColumn)
              .UseColumnTextForButtonValue) ||
          (OwningColumn is DataGridViewLinkColumn && 
          ((DataGridViewLinkColumn)OwningColumn).UseColumnTextForLinkValue))
          {
              AutomaticSortingEnabled = false;
              FilteringEnabled = false;
          }

          if (OwningColumn.SortMode == DataGridViewColumnSortMode.Automatic)
          {
              OwningColumn.SortMode = 
                  DataGridViewColumnSortMode.Programmatic;
          }
      }

      VerifyDataSource();
      HandleDataGridViewEvents();
      SetDropDownButtonBounds();
      base.OnDataGridViewChanged();
 }

Drop-Down ボタンの表示

ドロップダウン ボタンは、実際の ボタン コントロールではありません。 DataGridViewCell 型には、マウスのクリックを処理するためにオーバーライドできる OnMouseDown メソッドが用意されているため、Button コントロールは必要ありません。 [オートフィルター] セルはボタンを描画し、ヘッダーの適切な部分でクリックをチェックするだけです。

Paint メソッド

Paint メソッドは、ヘッダー セルのドロップダウン ボタンを描画します。 ドロップダウン ボタンの外観には、いくつかの要因が影響します。

  • ドロップダウン ボタンのサイズは、ヘッダー テキストの高さに基づきます。
  • ドロップダウン ボタンの場所は、ヘッダー セルの場所と DataGridView.RightToLeft プロパティの値に基づいています。
  • アプリケーションが現在テーマに設定されている場合は、ドロップダウン ボタンもテーマに設定されます。
  • が現在表示されている場合 dropDownListBox は、ドロップダウン ボタンが押された状態で表示されます。
  • 列が現在フィルター処理されている場合は、ドロップダウン ボタンが強調表示されます。
  • セル FilteringEnabled のプロパティが false の場合、ドロップダウン ボタンは表示されません。

これらの要因は、アプリケーションの実行中に変更される可能性があるため、ほとんどの要素は Paint メソッドが呼び出されるときに決定する必要があります。 ただし、サイズと場所は比較的一定のままであるため、これらの値はプロパティに DropDownButtonBounds 格納され、変更されない限り再利用されます。 プロパティは DropDownButtonBounds 、ユーザーがヘッダー セルをクリックしてドロップダウン リストを表示できる領域も示します。

ボタンのサイズと場所は、ヘッダー セルのサイズまたは場所が変更されるたびに変更する必要があります。 これは、 DataGridView コントロールを水平方向にスクロールする、列または列ヘッダーのサイズを変更する、コントロール自体のサイズを変更する (たとえば、ドックに入った DataGridView コントロールを含むフォームのサイズを変更する) などのユーザー 操作が発生する可能性があります。 前述のように、 OnDataGridViewChanged メソッドはハンドラーをさまざまな DataGridView イベントにアタッチします。 スクロールとサイズ変更に関連するイベントのハンドラーは、 メソッドを InvalidateDropDownButtonBounds 呼び出します。このメソッドは、ボタンの境界を Rectangle.Empty に設定するだけです。

Paint メソッドが呼び出されると、値が EmptyDropDownButtonBounds場合は、 SetDropDownButtonBounds メソッドが呼び出されて値がDropDownButtonBounds初期化されます。

Paint メソッドのソース コードは次のとおりです。


  protected override void Paint(
      Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, 
      int rowIndex, DataGridViewElementStates cellState, 
      object value, object formattedValue, string errorText, 
      DataGridViewCellStyle cellStyle, 
      DataGridViewAdvancedBorderStyle advancedBorderStyle, 
      DataGridViewPaintParts paintParts)
  {
      // Use the base method to paint the default appearance. 
      base.Paint(graphics, clipBounds, cellBounds, rowIndex, 
          cellState, value, formattedValue, 
          errorText, cellStyle, advancedBorderStyle, paintParts);

      // Continue only if filtering is enabled and ContentBackground is 
      // part of the paint request. 
      if (!FilteringEnabled || 
          (paintParts & DataGridViewPaintParts.ContentBackground) == 0)
      {
          return;
      }

      // Retrieve the current button bounds. 
      Rectangle buttonBounds = DropDownButtonBounds;

      // Continue only if the buttonBounds is big enough to draw.
      if (buttonBounds.Width < 1 || buttonBounds.Height < 1) return;

      // Paint the button manually or using visual styles if visual styles 
      // are enabled, using the correct state depending on whether the 
      // filter list is showing and whether there is a filter in effect 
      // for the current column. 
      if (Application.RenderWithVisualStyles)
      {
          ComboBoxState state = ComboBoxState.Normal;

          if (dropDownListBoxShowing)
          {
              state = ComboBoxState.Pressed;
          }
          else if (filtered)
          {
              state = ComboBoxState.Hot;
          }
          ComboBoxRenderer.DrawDropDownButton(
              graphics, buttonBounds, state);
      }
      else
      {
          // Determine the pressed state in order to paint the button 
          // correctly and to offset the down arrow. 
          Int32 pressedOffset = 0;
          PushButtonState state = PushButtonState.Normal;
          if (dropDownListBoxShowing)
          {
              state = PushButtonState.Pressed;
              pressedOffset = 1;
          }
          ButtonRenderer.DrawButton(graphics, buttonBounds, state);

          // If there is a filter in effect for the column, paint the 
          // down arrow as an unfilled triangle. If there is no filter 
          // in effect, paint the down arrow as a filled triangle.
          if (filtered)
          {
              graphics.DrawPolygon(SystemPens.ControlText, new Point[] {
                  new Point(
                      buttonBounds.Width / 2 + 
                          buttonBounds.Left - 1 + pressedOffset, 
                      buttonBounds.Height * 3 / 4 + 
                          buttonBounds.Top - 1 + pressedOffset),
                  new Point(
                      buttonBounds.Width / 4 + 
                          buttonBounds.Left + pressedOffset,
                      buttonBounds.Height / 2 + 
                          buttonBounds.Top - 1 + pressedOffset),
                  new Point(
                      buttonBounds.Width * 3 / 4 + 
                          buttonBounds.Left - 1 + pressedOffset,
                      buttonBounds.Height / 2 + 
                          buttonBounds.Top - 1 + pressedOffset)
              });
          }
          else
          {
              graphics.FillPolygon(SystemBrushes.ControlText, new Point[] {
                  new Point(
                      buttonBounds.Width / 2 + 
                          buttonBounds.Left - 1 + pressedOffset, 
                      buttonBounds.Height * 3 / 4 + 
                          buttonBounds.Top - 1 + pressedOffset),
                  new Point(
                      buttonBounds.Width / 4 + 
                          buttonBounds.Left + pressedOffset,
                      buttonBounds.Height / 2 + 
                          buttonBounds.Top - 1 + pressedOffset),
                  new Point(
                      buttonBounds.Width * 3 / 4 + 
                          buttonBounds.Left - 1 + pressedOffset,
                      buttonBounds.Height / 2 + 
                          buttonBounds.Top - 1 + pressedOffset)
              });
          }
      }

  }

SetDropDownButtonBounds メソッドと AdjustPadding メソッド

メソッドは SetDropDownButtonBounds 値を初期化します DropDownButtonBounds 。 ドロップダウン ボタンのサイズは、1 行のヘッダー テキストに対して優先されるヘッダー セルの高さに基づいています。 ボタンの位置は、セルの右下隅、または右から左の環境では左下に配置されます。 サイズと場所の両方が調整され、ビジュアル スタイルが有効になっているかどうかによって異なる視覚的なオフセットが提供されます。

メソッドは SetDropDownButtonBounds 、 メソッドを AdjustPadding 呼び出して、ドロップダウン ボタンの幅に基づいて、セルに対して有効な DataGridViewCellStyle.Padding プロパティを変更します。 パディング調整を使用すると、列のサイズを自動的に変更するときや並べ替えグリフを表示するときに、 DataGridView コントロールでドロップダウン ボタンの幅を考慮できます。 Padding 調整のため、セルの GetPreferredSize メソッドをオーバーライドして、自動サイズ設定をカスタマイズする必要はありません。

メソッドと AdjustPadding メソッドのSetDropDownButtonBoundsソース コードは次のとおりです。


  private void SetDropDownButtonBounds()
  {
      // Retrieve the cell display rectangle, which is used to 
      // set the position of the drop-down button. 
      Rectangle cellBounds = 
          this.DataGridView.GetCellDisplayRectangle(
          this.ColumnIndex, -1, false);

      // Initialize a variable to store the button edge length,
      // setting its initial value based on the font height. 
      Int32 buttonEdgeLength = this.InheritedStyle.Font.Height + 5;

      // Calculate the height of the cell borders and padding.
      Rectangle borderRect = BorderWidths(
          this.DataGridView.AdjustColumnHeaderBorderStyle(
          this.DataGridView.AdvancedColumnHeadersBorderStyle,
          new DataGridViewAdvancedBorderStyle(), false, false));
      Int32 borderAndPaddingHeight = 2 +
          borderRect.Top + borderRect.Height +
          this.InheritedStyle.Padding.Vertical;
      Boolean visualStylesEnabled =
          Application.RenderWithVisualStyles &&
          this.DataGridView.EnableHeadersVisualStyles;
      if (visualStylesEnabled) 
      {
          borderAndPaddingHeight += 3;
      }

      // Constrain the button edge length to the height of the 
      // column headers minus the border and padding height. 
      if (buttonEdgeLength >
          this.DataGridView.ColumnHeadersHeight -
          borderAndPaddingHeight)
      {
          buttonEdgeLength =
              this.DataGridView.ColumnHeadersHeight -
              borderAndPaddingHeight;
      }

      // Constrain the button edge length to the
      // width of the cell minus three.
      if (buttonEdgeLength > cellBounds.Width - 3)
      {
          buttonEdgeLength = cellBounds.Width - 3;
      }

      // Calculate the location of the drop-down button, with adjustments
      // based on whether visual styles are enabled. 
      Int32 topOffset = visualStylesEnabled ? 4 : 1;
      Int32 top = cellBounds.Bottom - buttonEdgeLength - topOffset;
      Int32 leftOffset = visualStylesEnabled ? 3 : 1;
      Int32 left = 0;
      if (this.DataGridView.RightToLeft == RightToLeft.No)
      {
          left = cellBounds.Right - buttonEdgeLength - leftOffset;
      }
      else
      {
          left = cellBounds.Left + leftOffset;
      }

      // Set the dropDownButtonBoundsValue value using the calculated 
      // values, and adjust the cell padding accordingly.  
      dropDownButtonBoundsValue = new Rectangle(left, top, 
          buttonEdgeLength, buttonEdgeLength);
      AdjustPadding(buttonEdgeLength + leftOffset);
  }

  private void AdjustPadding(Int32 newDropDownButtonPaddingOffset)
  {
      // Determine the difference between the new and current 
      // padding adjustment.
      Int32 widthChange = newDropDownButtonPaddingOffset - 
          currentDropDownButtonPaddingOffset;

      // If the padding needs to change, store the new value and 
      // make the change.
      if (widthChange != 0)
      {
          // Store the offset for the drop-down button separately from 
          // the padding in case the client needs additional padding.
          currentDropDownButtonPaddingOffset = 
              newDropDownButtonPaddingOffset;
        
          // Create a new Padding using the adjustment amount, then add it
          // to the cell's existing Style.Padding property value. 

          Padding dropDownPadding = new Padding(0, 0, widthChange, 0);
          this.Style.Padding = Padding.Add(
              this.InheritedStyle.Padding, dropDownPadding);
      }
  }

Drop-Down フィルター リストを使用したユーザー操作の表示、非表示、処理

ドロップダウン リストは通常、ユーザーがドロップダウン ボタンをクリックしたときに表示されます。 OnMouseDown メソッドは、列ヘッダーに対するユーザーのマウス クリックを処理します。 ユーザーが列ヘッダーをクリックしても、マウス クリックが プロパティで DropDownButtonBounds 指定された境界内になく、自動並べ替えが有効になっている場合は、列が並べ替えられて、ドロップダウン ボタンの横に並べ替えグリフが表示されます。 マウス クリックが 内 DropDownButtonBounds にあり、ドロップダウン リストがまだ表示されていない場合は、 メソッドを ShowDropDownList 使用して表示されます。

前に説明したように、 DataGridView.KeyDown イベントを処理して、ユーザーが Alt + ↓ などの特定のキーの組み合わせを押したときにドロップダウン リストを表示することもできます。 正しいキーストロークが検出されると、イベント ハンドラーは メソッドを ShowDropDownList 呼び出してドロップダウン リストを表示します。

ドロップダウン リストが表示されたら、ユーザーはキーボードを使用してリスト内を移動したり、スクロール バーが表示されている場合はリストをスクロールしたり、キーボードまたはマウスを使用して値を選択したり、他の場所をクリックしたりできます。

フィルター オプションをクリックするか、オプションを選択して Enter キーを押すと、 メソッドが呼び出されます UpdateFilter (次のセクションで説明します)。HideDropDownList ドロップダウン リスト以外の場所をクリックして、ドロップダウン リストが入力フォーカス呼び出し HideDropDownListを失うようにします。これにより、リスト コントロールを dropDownListBox 非表示にして DataGridView コントロールから削除するだけでなく、イベント ハンドラーがイベントから削除されます。 メソッドは HideDropDownList 、ユーザーが Esc キーを押したとき、またはドロップダウン リストの場所または内容を変更する DataGridView イベントが発生したときにも呼び出されます。

ToolStrip 関連のコントロールなどの一部の UI 要素では入力フォーカスがキャプチャされないため、特定の DataGridView イベント ハンドラーでドロップダウン リストを非表示にする必要があります。 このようなコントロールをユーザーが操作すると、 DataGridView コントロールまたはその内容が変更された場合は、ドロップダウン リストを表示し続けるのが不適切な可能性があります。 たとえば、前に説明したように 、StatusStrip コントロールの [すべて表示] リンクをクリックすると、データ ソースはフィルター処理されません。 この場合、ドロップダウン リストがまだ表示されている場合は、前のフィルター設定に基づいて不適切なフィルター オプションが含まれている可能性があります。また、フィルターの変更によって複数の列のサイズが変更された場合は、誤った場所に表示される可能性もあります。

ShowDropDownList メソッド

ドロップダウン リストの表示の詳細は、 メソッドにあります ShowDropDownList 。 このメソッドは、次のアクションを実行します。

  1. コレクションに dropDownListBox.Items フィルター オプションを設定します。 主なタスクは、データ ソースから値を取得することです。 このタスクは、次の PopulateFilters セクションで説明する メソッドによって処理されます。 フィルター値が取得された後、メソッドはそれらを Items コレクションに追加し、ShowDropDownList列に有効なフィルター値がある場合は現在のフィルター値を強調表示します。
  2. dropDownListBox.Boundsコンテンツとその他のいくつかの要因にdropDownListBox基づいてプロパティを設定します。 このアクションは、 メソッドによって SetDropDownListBoxBounds 処理されます。これについては、次のセクションで説明します。
  3. イベント ハンドラーをイベントに dropDownListBox 関連付け、ドロップダウン リストを使用してユーザーの操作を管理します。
  4. DataGridView コントロールで新しく構成dropDownListBoxされた を表示します。

ドロップダウン リストが表示されるとすぐに、ユーザーは前述のように操作できます。

メソッドのソース コードは ShowDropDownList 次のとおりです。


  public void ShowDropDownList()
  {
      PopulateFilters();

      String[] filterArray = new String[filters.Count];
      filters.Keys.CopyTo(filterArray, 0);
      dropDownListBox.Items.Clear();
      dropDownListBox.Items.AddRange(filterArray);
      dropDownListBox.SelectedItem = selectedFilterValue;

      HandleDropDownListBoxEvents();

      SetDropDownListBoxBounds();
      dropDownListBox.Visible = true;
      dropDownListBoxShowing = true;
      this.DataGridView.Controls.Add(dropDownListBox);
      dropDownListBox.Focus();

      // Invalidate the cell so that the drop-down button will repaint
      // in the pressed state. 
      this.DataGridView.InvalidateCell(this);
  }

PopulateFilters メソッド

特定の列のドロップダウン フィルター リストには、列に表示される各値のコピーが 1 つ含まれている必要があります。 これは、列が現在フィルターを適用していないと想定しています。 列をフィルター処理する場合は、フィルター処理されていない場合と同じリスト値になるように、フィルターを無視する必要があります。 フィルター リストには、列の独自のフィルターに関係なく列に表示できる値のみが含まれているため、列の独自のフィルター値が有効ではない場合でも、他のすべての列のフィルター値は有効なままです。 

dropDownFilterList 表示される値は、 DataGridView コントロールに表示されるのと同じように書式設定する必要があります。 ただし、フィルター値を選択した場合、その書式設定された値は BindingSource.Filter プロパティと常に互換性があるわけではありません。 このため、書式設定された値と書式設定されていない値の両方の文字列表現は、 という filtersOrderedDictionary インスタンスに格納されます。 OrderedDictionary は、ディクショナリの ShowDropDownListKeys コレクションを通じて書式設定された値に正しい順序でアクセスしてメソッドが設定dropDownListBoxできるように使用されます。 フィルター値を選択すると、 UpdateFilter 書式設定された表示値をディクショナリ キーとして使用して、 BindingSource.Filters プロパティと互換性のある書式設定されていない値を取得できます。

必要なフィルター値を取得するために、 メソッドは PopulateFilters 次のアクションを実行します。

  1. BindingSource.RaiseListChangedEvents プロパティを false に設定します。 これにより、PopulateFiltersDataGridView の表示を更新せずに、メソッドで現在の BindingSource.Filter プロパティ値を変更できます。
  2. 現在の Filter 値をキャッシュします。
  3. メソッドを FilterWithoutCurrentColumn 呼び出します。このメソッドは、現在の列に関連する部分を削除することで、現在のフィルター文字列を解析します。
  4. Filter プロパティを解析された値に設定します。
  5. フィルターディクショナリをクリアします。
  6. BindingSource の各行の現在の列の値を取得し、ArrayList に追加します。 Null 値と DBNull.Value 値 は除外されますが、その存在は後で確認できます。
  7. ArrayList を並べ替えます。 ArrayList.Sort メソッドは、値型の IComparable 実装を使用するため、文字列はカレンダーの順序に従ってアルファベット順、数値、および DateTime 値で並べ替えられます。
  8. 並べ替えられた ArrayList 内の各値について、 DataGridViewCell.GetFormattedValue メソッドを呼び出し、列の InheritedStyle プロパティ値を渡すことによって、書式設定された文字列表現を決定します。 これにより、 DataGridView セルと同じ書式の値がドロップダウン リストに確実に表示されます。 
  9. 各書式設定された値と、各値の書式設定されていない文字列表現をフィルター ディクショナリに追加します (空の文字列を除き、存在を除く)。
  10. フィルター ディクショナリに特別なフィルター オプションを次のように追加します。
    • 常にリストの最初の項目として (すべて) を追加します。
    • 列に空の文字列 (または null) と空でない文字列の両方が含まれている場合は、リストの末尾に (空白) と (NonBlanks) を追加します。
    • BindingSource.Filter プロパティをキャッシュされた値に復元します。
    • 通常の操作を再開するには、 BindingSource.RaiseListChangedEvents プロパティを true に設定します。

メソッドのソース コードは PopulateFilters 次のとおりです。


  private void PopulateFilters()
  {
      if (this.DataGridView == null) return;
  
      // Cast the data source to a BindingSource. 
      BindingSource data = this.DataGridView.DataSource as BindingSource;

      // Prevent the data source from notifying the DataGridView of changes. 
      data.RaiseListChangedEvents = false;

      // Cache the current BindingSource.Filter value and then change 
      // the Filter property to temporarily remove any filter for the 
      // current column. 
      String oldFilter = data.Filter;
      data.Filter = FilterWithoutCurrentColumn(oldFilter);

      // Reset the filters dictionary and initialize some flags
      // to track whether special filter options are needed. 
      filters.Clear();
      Boolean containsBlanks = false;
      Boolean containsNonBlanks = false;

      // Initialize an ArrayList to store the values in their original
      // types. This enables the values to be sorted appropriately.  
      ArrayList list = new ArrayList(data.Count);

      // Retrieve each value and add it to the ArrayList if it isn't
      // already present. 
      foreach (Object item in data)
      {
          Object value = null;

          // Use the ICustomTypeDescriptor interface to retrieve properties
          // if it is available; otherwise, use reflection. The 
          // ICustomTypeDescriptor interface is useful to customize

          // which values are exposed as properties. For example, the 
          // DataRowView class implements ICustomTypeDescriptor to expose 
          // cell values as property values.        
          // 
          // Iterate through the property names to find a case-insensitive
          // match with the DataGridViewColumn.DataPropertyName value.
          // This is necessary because DataPropertyName is case-
          // insensitive, but the GetProperties and GetProperty methods
          // used below are case-sensitive.
          ICustomTypeDescriptor ictd = item as ICustomTypeDescriptor;
          if (ictd != null)
          {
              PropertyDescriptorCollection properties = 
                  ictd.GetProperties();
              foreach (PropertyDescriptor property in properties)
              {
                  if (String.Compare(this.OwningColumn.DataPropertyName,
                      property.Name, true /*case insensitive*/,
                      System.Globalization.CultureInfo.InvariantCulture)

                      == 0)
                  {
                      value = property.GetValue(item);
                      break;
                  }
              }
          }
          else
          {
              PropertyInfo[] properties = item.GetType().GetProperties(
                  BindingFlags.Public | BindingFlags.Instance);
              foreach (PropertyInfo property in properties)
              {
                  if (String.Compare(this.OwningColumn.DataPropertyName,
                      property.Name, true /*case insensitive*/,
                     System.Globalization.CultureInfo.InvariantCulture) 
                      == 0)
                  {
                      value = property.GetValue(item, 
                          null /*property index*/);
                      break;
                  }
              }
          }

          // Skip empty values, but note that they are present. 
          if (value == null || value == DBNull.Value)
          {
              containsBlanks = true;
              continue;
          }

          // Add values to the ArrayList if they are not already there.
          if (!list.Contains(value))
          {
              list.Add(value);
          }
      }

      // Sort the ArrayList. The default Sort method uses the IComparable 
      // implementation of the stored values so that string, numeric, and 
      // date values will all be sorted correctly. 
      list.Sort();

      // Convert each value in the ArrayList to its formatted representation
      // and store both the formatted and unformatted string representations
      // in the filters dictionary. 
      foreach (Object value in list)
      {
          // Use the cell's GetFormattedValue method with the column's
          // InheritedStyle property so that the dropDownListBox format
          // will match the display format used for the column's cells. 
          String formattedValue = null;
          DataGridViewCellStyle style = OwningColumn.InheritedStyle;
          formattedValue = (String)GetFormattedValue(value, -1, ref style, 
              null, null, DataGridViewDataErrorContexts.Formatting);

          if (String.IsNullOrEmpty(formattedValue))
          {
              // Skip empty values, but note that they are present.
              containsBlanks = true;
          }
          else if (!filters.Contains(formattedValue))
          {
              // Note whether non-empty values are present. 
              containsNonBlanks = true;

              // For all non-empty values, add the formatted and 
              // unformatted string representations to the filters 
              // dictionary.
              filters.Add(formattedValue, value.ToString());
          }
      }

      // Restore the filter to the cached filter string and 
      // re-enable data source change notifications. 
      if (oldFilter != null) data.Filter = oldFilter;
      data.RaiseListChangedEvents = true;

      // Add special filter options to the filters dictionary
      // along with null values, since unformatted representations
      // are not needed. 
      filters.Insert(0, "(All)", null);
      if (containsBlanks && containsNonBlanks)
      {
          filters.Add("(Blanks)", null);
          filters.Add("(NonBlanks)", null);
      }
  }

SetDropDownListBoxBounds メソッド

メソッドは SetDropDownListBoxBounds 、ドロップダウン リストのサイズと場所を初期化します。 推奨されるサイズは、主に、ディクショナリの dropDownListBoxKeys コレクションに格納されている書式設定された値であるコンテンツに filters 依存します。 メソッドは SetDropDownListBoxBounds 、最初に各フィルター値に対して Graphics.MeasureString メソッドを呼び出します。 各値について、幅が前のすべての値よりも広い場合に格納され、高さはすべての値の累積合計高さに追加されます。 その後、結果を使用して、推奨されるサイズを決定します。

推奨される高さは、次の値の中で最も小さい値です。

  • すべてのフィルター値の累積高さ。
  • プロパティ値から計算されるユーザー指定の DropDownListBoxMaxLines 最大高さ。
  • DataGridView コントロール クライアント領域の使用可能な高さ。

推奨される幅は、最も幅の広いフィルター値の幅と、優先する高さがすべての dropDownListBox 項目を表示しない場合はスクロール バーの幅、パディングには少量を加えた値です。

推奨される dropDownListBox 場所は、ドロップダウン ボタンの場所と DataGridView コントロールの端に基づいています。 リストの右端は、ドロップダウン ボタンの右端に合わせるのが理想的です。 RightToLeft が有効になっている場合、リストの左端はボタンの左端に配置されます。 ただし、リストをボタンに合わせて配置すると、 DataGridView コントロールの端と重なる場合は、リストの重複する端がコントロールの端にある必要があります。

サイズと場所を dropDownListBox 指定すると、ドロップダウン リストを表示する準備が整います。

メソッドのソース コードは SetDropDownListBoxBounds 次のとおりです。


  private void SetDropDownListBoxBounds()
  {
      // Declare variables that will be used in the calculation, 
      // initializing dropDownListBoxHeight to account for the 
      // ListBox borders.
      Int32 dropDownListBoxHeight = 2;
      Int32 currentWidth = 0;
      Int32 dropDownListBoxWidth = 0;
      Int32 dropDownListBoxLeft = 0;

      // For each formatted value in the filters dictionary Keys collection,
      // add its height to dropDownListBoxHeight and, if it is wider than 
      // all previous values, set dropDownListBoxWidth to its width.
      using (Graphics graphics = dropDownListBox.CreateGraphics())
      {
          foreach (String filter in filters.Keys)
          {
              SizeF stringSizeF = graphics.MeasureString(
                  filter, dropDownListBox.Font);
              dropDownListBoxHeight += (Int32)stringSizeF.Height;
              currentWidth = (Int32)stringSizeF.Width;
              if (dropDownListBoxWidth < currentWidth)
              {
                  dropDownListBoxWidth = currentWidth;
              }
          }
      }

      // Increase the width to allow for horizontal margins and borders. 
      dropDownListBoxWidth += 6;

      // Constrain the dropDownListBox height to the 
      // DropDownListBoxMaxHeightInternal value, which is based on 

      // the DropDownListBoxMaxLines property value but constrained by
      // the maximum height available in the DataGridView control.
      if (dropDownListBoxHeight > DropDownListBoxMaxHeightInternal)
      {
          dropDownListBoxHeight = DropDownListBoxMaxHeightInternal;

          // If the preferred height is greater than the available height,
          // adjust the width to accommodate the vertical scroll bar. 
          dropDownListBoxWidth += SystemInformation.VerticalScrollBarWidth;
      }

      // Calculate the ideal location of the left edge of dropDownListBox 
      // based on the location of the drop-down button and taking the 
      // RightToLeft property value into consideration. 
      if (this.DataGridView.RightToLeft == RightToLeft.No)
      {
          dropDownListBoxLeft = DropDownButtonBounds.Right - 
              dropDownListBoxWidth + 1;
      }
      else
      {
          dropDownListBoxLeft = DropDownButtonBounds.Left - 1;
      }

      // Determine the left and right edges of the available horizontal
      // width of the DataGridView control. 
      Int32 clientLeft = 1;
      Int32 clientRight = this.DataGridView.ClientRectangle.Right;
      if (this.DataGridView.DisplayedRowCount(false) < 
          this.DataGridView.RowCount)
      {
          if (this.DataGridView.RightToLeft == RightToLeft.Yes)
          {
              clientLeft += SystemInformation.VerticalScrollBarWidth;
          }
          else
          {
              clientRight -= SystemInformation.VerticalScrollBarWidth;
          }
      }

      // Adjust the dropDownListBox location and/or width if it would
      // otherwise overlap the left or right edge of the DataGridView.
      if (dropDownListBoxLeft < clientLeft)
      {
          dropDownListBoxLeft = clientLeft;
      }
      Int32 dropDownListBoxRight =  
          dropDownListBoxLeft + dropDownListBoxWidth + 1;
      if (dropDownListBoxRight > clientRight)
      {
          if (dropDownListBoxLeft == clientLeft)
          {
              dropDownListBoxWidth -=
                  dropDownListBoxRight - clientRight;
          }
          else
          {
              dropDownListBoxLeft -=
                  dropDownListBoxRight - clientRight;
              if (dropDownListBoxLeft < clientLeft)
              {
                  dropDownListBoxWidth -= clientLeft - dropDownListBoxLeft;
                  dropDownListBoxLeft = clientLeft;
              }
          }
      }

      // Set the ListBox.Bounds property using the calculated values. 
      dropDownListBox.Bounds = new Rectangle(dropDownListBoxLeft,
          DropDownButtonBounds.Bottom, // top of drop-down list box
          dropDownListBoxWidth, dropDownListBoxHeight);
  }

ListBox イベントの処理

処理が必要なイベントは dropDownListBoxLostFocusMouseClickおよび KeyDown イベントです。 ドロップダウン リストが入力フォーカスを失うように、ユーザーがドロップダウン リスト以外の場所をクリックすると、 LostFocus イベント ハンドラーは メソッドを HideDropDownList 呼び出します。 ユーザーがフィルター オプションをクリックするか Enter キーを押すと、 MouseClick イベント ハンドラーまたは KeyDown イベント ハンドラーは メソッドを UpdateFilter 呼び出し、 メソッドを HideDropDownList 呼び出します。 ユーザーが Esc キーを押すと、 KeyDown イベント ハンドラーは メソッドのみを HideDropDownList 呼び出します。

バインドされたデータのフィルター処理

ユーザーが特定の列のドロップダウン リストからフィルター オプションをクリックすると、その列で選択した値を持つ行のみが表示されるようにデータ ソースがフィルター処理されます。 データ ソース全体には BindingSource.Filter プロパティが 1 つしかないため、選択したフィルター オプションをすべての列のフィルター オプションと組み合わせて 1 つのフィルター文字列にする必要があります。

フィルター文字列 UpdateFilterを操作するメソッドは、、 FilterWithoutCurrentColumnRemoveFilter、および GetFilterStatusの 4 つあります。

UpdateFilter メソッド

メソッドは UpdateFilter 、ドロップダウン フィルター リストのユーザーの選択に応じて BindingSource.Filter 値を変更します。 これを行うには、次のアクションを実行します。

  1. メソッドを FilterWithoutCurrentColumn 呼び出して、現在の列のフィルター値を含まない解析済みのフィルター文字列を取得します。
  2. ユーザーが [(すべて)] オプションを選択した場合は、Filter プロパティを解析された値に設定します。
  3. ユーザーが (すべて) 以外のオプションを選択した場合は、列のフィルター文字列を作成し、現在の Filter 値に追加します。

通常のフィルター値のフィルター文字列は次の形式です。


  [columnName]='filterValue'

(Blanks) オプションと (NonBlanks) オプションの場合、フィルター文字列は、null 値に空の文字列を使用して値を文字列に変換し、変換後の値が長さが 0 であるかどうかをテストします。 これらのフィルター文字列の形式は次のとおりです。


  LEN(ISNULL(CONVERT([columnName],'System.String'),''))=0
  LEN(ISNULL(CONVERT([columnName],'System.String'),''))>0

BindingSource.Filter プロパティの値が null または空の場合、メソッドはそれをUpdateFilter列フィルター文字列に設定します。 Filter プロパティが null または空でない場合、列フィルターは Filter 値の末尾に追加され、既存の値から文字列 " AND "で区切られます。

メソッドのソース コードは UpdateFilter 次のとおりです。


  private void UpdateFilter()
  {
      // Continue only if the selection has changed.
      if (dropDownListBox.SelectedItem.ToString()
          .Equals(selectedFilterValue))
      {
          return;
      }

      // Store the new selection value. 
      selectedFilterValue = dropDownListBox.SelectedItem.ToString();

      // Cast the data source to an IBindingListView.
      IBindingListView data = 
          this.DataGridView.DataSource as IBindingListView;

      // If the user selection is (All), remove any filter currently 
      // in effect for the column. 
      if (selectedFilterValue.Equals("(All)"))
      {
          data.Filter = FilterWithoutCurrentColumn(data.Filter);
          filtered = false;
          currentColumnFilter = String.Empty;
          return;
      }

      // Declare a variable to store the filter string for this column.
      String newColumnFilter = null;

      // Store the column name in a form acceptable to the Filter property, 
      // using a backslash to escape any closing square brackets. 

      String columnProperty = 
          OwningColumn.DataPropertyName.Replace("]", @"\]");

      // Determine the column filter string based on the user selection.
      // For (Blanks) and (NonBlanks), the filter string determines whether
      // the column value is null or an empty string. Otherwise, the filter
      // string determines whether the column value is the selected value. 
      switch (selectedFilterValue)
      {
          case "(Blanks)":
              newColumnFilter = String.Format(
                  "LEN(ISNULL(CONVERT([{0}],'System.String'),''))=0",
                  columnProperty);
              break;
          case "(NonBlanks)":
              newColumnFilter = String.Format(
              "LEN(ISNULL(CONVERT([{0}],'System.String'),''))>0",
                  columnProperty);
              break;
          default:
              newColumnFilter = String.Format("[{0}]='{1}'",
                  columnProperty,
                  ((String)filters[selectedFilterValue])
                  .Replace("'", "''"));  
              break;
      }

      // Determine the new filter string by removing the previous column 
      // filter string from the BindingSource.Filter value, then appending 
      // the new column filter string, using " AND " as appropriate. 
      String newFilter = FilterWithoutCurrentColumn(data.Filter);
      if (String.IsNullOrEmpty(newFilter))
      {
          newFilter += newColumnFilter;
      }
      else
      {
          newFilter += " AND " + newColumnFilter;
      }


      // Set the filter to the new value.
      try
      {
          data.Filter = newFilter;
      }
      catch (InvalidExpressionException ex)
      {
          throw new NotSupportedException(
              "Invalid expression: " + newFilter, ex);
      }

      // Indicate that the column is currently filtered
      // and store the new column filter for use by subsequent
      // calls to the FilterWithoutCurrentColumn method. 
      filtered = true;
      currentColumnFilter = newColumnFilter;
  }

FilterWithoutCurrentColumn メソッド

メソッドは FilterWithoutCurrentColumn 、現在の列のフィルターを削除するために、特定のフィルター文字列を解析します。 BindingSource.Filter プロパティを直接変更することはありませんが、このメソッドは、 メソッドと PopulateFilters メソッドによって UpdateFilterFilter プロパティを変更するために使用されます。

フィルター文字列を解析するために、 FilterWithoutCurrentColumn メソッドは フィールドを currentColumnFilter 使用します。このフィールドには 、BindingSource.Filter プロパティ値の現在の列の部分のみが格納されます。 メソッドは FilterWithoutCurrentColumn 、指定したフィルター文字列で値を currentColumnFilter 検索します。 列の値が見つかった場合は、指定した文字列のコピーが列値なしで返され、戻り値が有効なフィルター文字列になるのを防ぐ余分な " AND " 区切り記号は返しません。

メソッドのソース コードは FilterWithoutCurrentColumn 次のとおりです。


  private String FilterWithoutCurrentColumn(String filter)
  {
      // If there is no filter in effect, return String.Empty. 
      if (String.IsNullOrEmpty(filter))
      {
          return String.Empty;
      }

        // If the column is not filtered, return the filter string unchanged. 
      if (!filtered)
      {
          return filter;
      }

      if (filter.IndexOf(currentColumnFilter) > 0)
      {
          // If the current column filter is not the first filter, return
          // the specified filter value without the current column filter 
          // and without the preceding " AND ". 
          return filter.Replace(
              " AND " + currentColumnFilter, String.Empty);
      }
      else
      {
          if (filter.Length > currentColumnFilter.Length)
          {
              // If the current column filter is the first of multiple 
              // filters, return the specified filter value without the 
              // current column filter and without the subsequent " AND ". 
              return filter.Replace(
                  currentColumnFilter + " AND ", String.Empty);
          }
          else
          {
              // If the current column filter is the only filter, 
              // return the empty string.
              return String.Empty;
          }
      }
  }

RemoveFilter メソッドと GetFilterStatus メソッド

クライアント アプリケーションでフィルター状態文字列を表示し、[すべて表示] オプションを公開しやすくするために、静的 RemoveFilter メソッドと GetFilterStatus メソッドが用意されています。 これらは、個々のセルではなくデータ ソースに関連するため、 静的 です。 ただし、データ ソースは DataGridView コントロールごとに異なる場合があるため、これらのメソッドでは、クライアント アプリケーションが DataGridView コントロールへの参照を渡す必要があります。 これらのメソッドはセル クラスに実装されていますが、便宜上、 クラスを DataGridViewAutoFilterTextBoxColumn 介して公開されます。

メソッドは RemoveFilterBindingSource.Filter プロパティ値を null に設定して、すべてのフィルターを削除します。 このメソッドを使用すると、クライアント アプリケーションは Filter プロパティに直接アクセスしなくても、[すべて表示] オプションを実装できます。 クライアント アプリケーションでは、問題を引き起こさずに Filter プロパティを null または String.Empty に設定できますが、他の値に設定すると、ライブラリによって提供されるフィルター処理が DataGridViewAutoFilter 妨げられる可能性があります。 このため、 Filter プロパティの変更をクライアント コードから非表示にしておくことをお勧めします。

メソッドは GetFilterStatus 、フィルター処理された BindingSource.Count プロパティ値を含む状態文字列を返します。 フィルター処理されていないカウントを RemoveFilter 取得するには Filter プロパティの値を一時的に変更する必要があるため、このメソッドは メソッドよりも多くの作業を行います。 これは、 メソッドに対して前述したのと同じプロセスを PopulateFilters 使用して行います。 データ ソースが現在フィルター処理されていない場合、 メソッドは GetFilterStatus 空の文字列を返します。 それ以外の場合は、次の形式の文字列を返します。

見つかった filteredCount レコードの unfilteredCount

メソッドのソース コードは GetFilterStatus 次のとおりです。


  public static String GetFilterStatus(DataGridView dataGridView)
  {
      // Continue only if the specified value is valid. 
      if (dataGridView == null)
      {
          throw new ArgumentNullException("dataGridView");
      }

      // Cast the data source to a BindingSource.
      BindingSource data = dataGridView.DataSource as BindingSource;

      // Return String.Empty if there is no appropriate data source or
      // there is no filter in effect. 
      if (String.IsNullOrEmpty(data.Filter) ||
          data == null || 
          data.DataSource == null || 
          !data.SupportsFiltering)
      {
          return String.Empty;
      }

      // Retrieve the filtered row count. 
      Int32 currentRowCount = data.Count;

      // Retrieve the unfiltered row count by 
      // temporarily unfiltering the data.
      data.RaiseListChangedEvents = false;
      String oldFilter = data.Filter;
      data.Filter = null;
      Int32 unfilteredRowCount = data.Count;
      data.Filter = oldFilter;
      data.RaiseListChangedEvents = true;

      Debug.Assert(currentRowCount <= unfilteredRowCount, 
          "current count is greater than unfiltered count");

      // Return String.Empty if the filtered and unfiltered counts
      // are the same, otherwise, return the status string. 
      if (currentRowCount == unfilteredRowCount)
      {
          return String.Empty;
      }
      return String.Format("{0} of {1} records found", 
          currentRowCount, unfilteredRowCount);
  }

メソッドと GetFilterStatus メソッドの使用のRemoveFilter詳細については、この記事で前述した「ライブラリのDataGridViewAutoFilter使用」を参照してください。

[追加のプロパティ]

クラスはDataGridViewAutoFilterColumnHeaderCellDataGridViewColumnHeaderCell 基本クラス AutomaticSortingEnabled(、 FilteringEnabled、) から継承するプロパティに 3 つのプロパティを DropDownListBoxMaxLines追加します。

AutomaticSortingEnabledプロパティを使用すると、列 SortMode プロパティの値を Programmatic に保ちながら、自動並べ替えを無効にすることができます。 オートフィルター機能を使用した自動並べ替えでは、ドロップダウン ボタンをクリックして列を並べ替えないようにするために、 SortMode プロパティの値を プログラム で指定する必要があります。 ただし、並べ替えを自分で処理する場合は、 並べ替 えグリフの列ヘッダーの領域を予約するために、SortMode プロパティを Programmatic に設定したままにする必要があります。

FilteringEnabledプロパティを使用すると、フィルター処理を無効にすることができます。 が false の場合FilteringEnabled、ドロップダウン ボタンは表示されず、ヘッダーが表示され、通常の列ヘッダー セルのように動作します。

DropDownListBoxMaxLinesプロパティを使用すると、ドロップダウン リストに表示される行の最大数をカスタマイズできます。 実際の高さは DataGridView コントロールで使用できる高さによって制限されますが、このプロパティを使用すると、高さをさらに制限できます。

これらのセル プロパティは、 クラスを DataGridViewAutoFilterTextBoxColumn 介して公開されるため、便宜上公開されます。

DataGridViewAutoFilterTextBoxColumn クラスの実装の詳細

クラスは DataGridViewAutoFilterTextBoxColumn 、既存の列型に AutoFilter サポートを追加する方法の例として提供されます。 AutoFilter 列の種類は、この記事で前述したように、ユーザーが Windows フォーム Designerを使用してアプリケーションにオートフィルター機能を追加できるようにする場合に特に便利です。

クラスの DataGridViewAutoFilterTextBoxColumn 重要なコードはクラス コンストラクターです。 これは、DefaultHeaderCellType プロパティを型に DataGridViewAutoFilterColumnHeaderCell 設定し、 SortMode プロパティを Programmatic に設定して、ドロップダウン ボタンをクリックしても自動並べ替えをトリガーしないようにする以外は何も行いません。


  public DataGridViewAutoFilterTextBoxColumn() : base()
  {
      base.DefaultHeaderCellType = 
          typeof(DataGridViewAutoFilterColumnHeaderCell);
      base.SortMode = DataGridViewColumnSortMode.Programmatic;
  }

便宜上、 クラスの DataGridViewAutoFilterTextBoxColumn 残りのコードが提供されます。 DefaultHeaderCellType プロパティと SortMode プロパティは、デザイナーから非表示にし、SortMode プロパティが Automatic に設定されている場合に例外をスローするように再実装されます。 さらに、列クラスは、セル クラスの新しいパブリック プロパティと静的メソッドを公開します。 これらのメンバーは単にセル メンバーをラップするので、列インスタンスにプロパティ値を設定すると、列のヘッダー セル インスタンス内の同じプロパティが更新されます。

考えられる機能強化

ライブラリには DataGridViewAutoFilter 、列フィルター処理用の基本的なユーザー インターフェイスが用意されており、DataGridView 列ヘッダー セルをカスタマイズする方法が示されています。 また、既存のセル型から派生できない場合に、セル内でWindows フォーム コントロールをホストする方法も示します。

AutoFilter 機能として、ライブラリには改善の DataGridViewAutoFilter 可能性があるいくつかの領域が残されています。 次の一覧では、 クラスに対して行 DataGridViewAutoFilterColumnHeaderCell うことを検討する必要がある機能強化について説明します。

  • カスタム フィルター処理: Excel のようなカスタム フィルター ダイアログ ボックスを表示し、"contains"、"does not contain"、"begins with"などのフィルター値を指定できます。
  • 任意のデータ ソース、非バインド モード、または仮想モードでのフィルター処理。
  • 画像などの特殊なセル値のサポート。
  • 並べ替えられた列とその並べ替えの優先順位を示すヘッダー ラベルを持つ複数列の並べ替え機能など、追加のヘッダー セル機能との統合。

その他のリソース

この記事とライブラリに関DataGridViewAutoFilterするフィードバックを提供し、更新プログラムをチェックするには、Windows フォームドキュメント 更新ブログを参照してください。

次のリソースは、DataGridView セルのカスタマイズに関する追加情報を提供します。

一般的なWindows フォームの詳細については、次を参照してください。