Karl Erickson
Microsoft Corporation
July 2006
日本語版最終更新日 2006 年 9 月 28 日
適用対象:
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 つは、DataSet
にバインドされている BindingSource
コンポーネントに DataGridView
コントロールをバインドすることです。これは、Visual Studio 2005 の Windows フォーム デザイナを使用してデータ バインドを設定したときに作成される構成です。これにより、ユーザーの入力に応じて BindingSource.Filter
プロパティを設定できるようになります。
フォームにスペースの余裕があるときは、テキスト ボックスやコンボ ボックスを使用して、列フィルタ オプションを設定するためのユーザー インターフェイスを作成できます。ただし、スペースに余裕がなかったり、アプリケーションで列のフィルタ処理をよく行う場合には、Excel のオートフィルタ機能のように、列ヘッダーに直接、列フィルタ ドロップダウン リストを用意した方が便利です。これは、ドックが挿入されている DataGridView
を子フォームに表示する場合に特に役立ちます。
注: この記事の残りの部分では、Excel スタイルの列フィルタ ドロップダウン リストに対し、Excel の用語である "オートフィルタ" を使用します。
列フィルタ機能と DataGridView コントロール
DataGridView
コントロールになぜオートフィルタ機能が含まれていないのか、疑問に思われたことがあるかもしれません。DataGridView
の設計上の目標は、ほとんどすべてのユーザーには一般的な機能では対応できない特別なニーズがあることを認識した上で、最も一般的な機能をすべて網羅した、柔軟なグリッド コントロールを作成することでした。コントロールには基本的な機能をすべて含める一方で、外観と動作の両方において簡単にカスタマイズでき、置き換える DataGrid
と比較して大きな改善がなされている必要がありました。フォームの別の場所で UI を用意して、フィルタ機能を管理できるため、オートフィルタ機能は必要とされませんでした。オートフィルタ機能は、ユーザー自身が実装することもできます。ただし、これには高度なカスタマイズが必要です。
DataGridView
コントロールは、いくつかの異なるレベルでカスタマイズできます。たとえば、スタイル プロパティを設定してクライアント アプリケーションで描画イベントを処理したり、既存の機能を利用するために既存の列やセルの種類を拡張することによって、カスタムの列およびセルの種類を提供したりすることができます。たとえば、任意の Windows フォーム コントロールをホストする新しい編集用セルの種類の作成は、DataGridViewTextBoxCell
クラスを拡張すれば、比較的簡単です。
オートフィルタ機能を実装するには、編集コントロール機能を持たない DataGridViewColumnHeaderCell
クラスからの派生が必要です。このクラスを拡張してドロップダウン リストを表示することは、DataGridViewComboBoxCell
クラスを調整するよりも難しい作業です。代わりに、ドロップダウン ボタンの描画、マウス クリックのテスト、ドロップダウン リストの配置と表示、およびユーザーの選択に対する適切な応答というすべての作業を行う必要があります。
DataGridViewAutoFilter クラス ライブラリ
この記事に付属しているサンプルは、DataGridViewAutoFilter ライブラリの形式でオートフィルタ機能を実装する方法を示します。このライブラリには、DataGridViewAutoFilterColumnHeaderCell クラスおよびサポートする列クラスが含まれています。このサンプルには完全な機能が含まれているわけではなく、汎用でも、バグがないわけでも、Microsoft がサポートしているソリューションでもありませんが、このサンプルを基にして、変更や独自の実装を行うことができます。
図 1 は、DataGridViewAutoFilter ライブラリの動作を示しています。
図 1. DataGridViewAutoFilter ライブラリを使用して実装されたオートフィルタ機能
サンプルの機能と依存関係については、以下のセクションで詳しく説明されており、直ちに使用できるものや、ユーザーが用意する必要のあるものについて理解することができます。これらはすべての機能や制限を網羅したものではありませんが、さらに調査を進めるか、別のソリューションを探すかを決定するうえで役立ちます。サンプル コードを運用アプリケーションで使用する前に、サポートするすべてのシナリオを綿密にテストしてください。
機能
サンプルの DataGridViewAutoFilter ライブラリの機能は次のとおりです。
- 複数列のフィルタ機能のサポート。各ドロップダウン リストには、他の列のフィルタを考慮して、列内の一意の値すべてが表示されます。列がフィルタ処理される場合、列のドロップダウン リストには、フィルタ処理されない場合に列に表示される値が表示され、現在のフィルタ値が選択されます。
- 自動並べ替えのサポート。ユーザーは、ヘッダー セルをクリックして列を並べ替えることができます。ドロップダウン ボタンの横に、並べ替えグリフが表示されます。
- 次の特殊なフィルタ オプションのサポート: (すべて)、(空白セル)、および (空白以外のセル)。
- Visual Studio 2005 での設計時におけるアプリケーションへのオートフィルタ機能の追加のサポート。
実装されない機能
サンプルの DataGridViewAutoFilter ライブラリには、Excel で利用できるような機能や、特定のアプリケーションに必要な機能がない場合があります。実装されない機能は次のとおりです。
- ドロップダウン リストの並べ替えオプション。自動並べ替えがサポートされているので、この Excel の機能は必要ありません。
- (オプション...) や (トップテン...) など、追加の特殊なフィルタ オプション。これらのオプションは、このサンプルの対象外です。すべてのフィルタ機能は、特定の値、またはサポートされている特殊なフィルタ オプションのいずれかに関するものです。複雑なフィルタ オプションを用意する必要がある場合は、Excel のような、独自のカスタム オートフィルタ ダイアログ ボックスを実装できます。
- 非バインド モードまたは仮想モード、あるいはフィルタ機能を持たないデータ ソースを使用したバインド モードにおけるフィルタ機能のサポートはなし。この機能は、このサンプルの対象外です。フィルタ機能を持つ外部データ ソースを使用できない場合は、フィルタ機能を自分で実装する必要があります。
- 任意のデータ書式設定に対するサポートはなし。ドロップダウン リストに表示される文字列は、列のセル スタイルを使用して、DataGridViewCell.GetFormattedValue
プロパティを経由して取得されます。DataGridViewCellStyle.Format
プロパティを使用すると、日付または通貨の書式設定を指定できます。ただし、特殊な書式設定のニーズがある場合は、ユーザーが書式設定を実装する必要があります。
依存関係
サンプルの DataGridViewAutoFilter ライブラリは、次の 4 つの要件に依存しています。
- DataGridView.DataSource
を BindingSource
コンポーネントに設定する必要があります。
- BindingSource
コンポーネントを IBindingListView
の実装にバインドする必要があります。
- IBindingListView.SupportsFiltering
プロパティの値を true
に設定する必要があります。
- IBindingListView.Filter
プロパティは複数列のフィルタ機能をサポートする必要があります。
これらの 4 つの要件は、Visual Studio 2005 の Windows フォーム デザイナを使用して、ADO.NET でデータ バインドを設定した場合、自動的に満たされます (詳細については、MSDN2 ライブラリの「方法 : デザイナを使用してデータを Windows フォーム DataGridView コントロールにバインドする」を参照してください)。あるいは、要件を満たす任意の IBindingListView
実装にバインドすることもできます。
既存のデータ ソースにバインドする必要がある場合は、データ ソースを BindingSource
コンポーネントにバインドし、BindingSource
を DataGridView
コントロールにバインドする必要があります。BindingSource
コンポーネントに対する依存が必要な理由は、BindingSource.RaiseListChangedEvents
プロパティにあります。このプロパティを false
に設定すると、DataGridView
コントロールに表示されているデータを更新せずに、フィルタを一時的に変更できるようになります。これは、列自体がフィルタ処理されている場合でも、ドロップダウン リストに表示される値を計算するために必要です。代替データ ソースに同様の機能を用意できる場合には、この依存関係を回避できます。
フィルタ機能をサポートするには、SupportsFiltering
プロパティ値を true
に設定した状態で、BindingSource
を IBindingListView
にバインドする必要があります。これは、DataSet
などの ADO.NET オブジェクトにバインドする場合です (デザイナを使用してデータ バインドを設定する場合など)。DataSet
クラスは IBindingListView
自体を実装しませんが、BindingSource
は DataSet
内の適切な DataTable
から DataView
を取得できます。この場合、BindingSource.Filter
プロパティは、DataColumn.Expression
プロパティを利用して DataView.RowFilter
プロパティにマップされます。
IBindingListView
インターフェイスには、その Filter
プロパティの実装方法についての決まりはありませんが、Filter
プロパティを使用するアプリケーションは、フィルタ文字列の書式について特定の推測を行う必要があります。サンプルの DataGridViewAutoFilter ライブラリでは、.NET Framework 管理下の参照ドキュメントの DataColumn.Expression
のトピックに記載されている構文を使用したブール式のフィルタ文字列を IBindingListView.Filter
プロパティが受け入れるものと見なされます。
DataGridViewAutoFilter ライブラリを使用する
DataGridViewAutoFilter ライブラリは、プロジェクト内で参照して DataGridViewAutoFilterColumnHeaderCell および DataGridViewAutoFilterTextBoxColumn クラスにアクセスできるようにするための単一のアセンブリです。これらのクラスはプログラムで使用するか、Visual Studio 2005 の Windows フォーム デザイナで使用できます。
ヘッダー セルにオートフィルタ ドロップダウン ボタンを表示するには、列の HeaderCell
プロパティを DataGridViewAutoFilterColumnHeaderCell クラスのインスタンスに設定する必要があります。特定の列の HeaderCell
プロパティは、プログラムによって設定できます。ただし、Windows フォーム デザイナの [プロパティ]
ウィンドウで HeaderCell
プロパティを設定することはできません。
デザイナでの操作をサポートするために、DataGridViewAutoFilterTextBoxColumn が用意されています。デザイナでは、[列の編集]
および [列の追加] ダイアログ ボックスで、この列の種類を選択できます。DataGridViewAutoFilterTextBoxColumn は、DataGridViewTextBoxColumn
を拡張し、HeaderCell
プロパティを DataGridViewAutoFilterColumnHeaderCell クラスの新しいインスタンスに設定できるようにします。
1 つ以上のオートフィルタ ヘッダー セルをアプリケーションに追加した後で、現在のフィルタ状態に関するユーザーのフィードバックを提供し、すべての行を表示する手段をユーザーに与える必要が生じることがあります。
BindingSource コンポーネントを使用すれば、フィルタ処理された行数の取得やフィルタの削除ができますが、DataGridViewAutoFilter のセルおよび列クラスには、この機能を含む便利なメソッドが用意されています。
フォーム コードからドロップダウン リストにキーボードでアクセスする手段を用意する必要が生じることもあります。Excel では、ユーザーはドロップダウン ボタンがあるセルに移動して、Alt + 上方向キーまたは Alt + 下方向キーを押すことができます。ただし、DataGridView
コントロールでは、ユーザーはヘッダー セルに移動できません。その代わり、Alt + 上方向キーまたは Alt + 下方向キーによって、現在のセルを含んでいる列にドロップダウン リストを表示できるようにする必要があります。
以下の手順では、DataGridViewAutoFilter クラスを使用した 4 つのシナリオについて説明します。最初の手順では、デザイナを使用してオートフィルタ機能を Windows フォーム アプリケーションに追加する方法について説明します。2 番目の手順では、プログラムによってオートフィルタ機能を追加する方法について説明します。3 番目の手順では、フィルタ ステータス文字列と [すべて表示]
オプションを表示することによってクライアント アプリケーションを拡張する方法について説明します。最後に、4 番目の手順では、キーボードを使用してユーザーがドロップダウン リストを表示できるようにする方法について説明します。
デザイナを使用してオートフィルタ機能をアプリケーションに追加するには
- Windows アプリケーション プロジェクトで、DataGridViewAutoFilter.dll アセンブリへの参照を追加します。
- DataGridView
コントロールをフォームに追加し、コントロールのスマート タグの [データ ソースの選択]
タスクを使用して、データをコントロールにバインドします。詳細については、MSDN2 ライブラリの「方法 : デザイナを使用してデータを Windows フォーム DataGridView コントロールにバインドする」を参照してください。
- 列が生成されたら、コントロールのスマート タグで、[列の編集]
をクリックします。
- [列の編集]
ダイアログ ボックスで、列を選択します。
- ダイアログ ボックスの [プロパティ]
ウィンドウで、ColumnType
プロパティを選択し、ドロップダウン リストから
DataGridViewAutoFilterTextBoxColumn を選択します。
- オートフィルタ機能を使用するすべての列に対して、手順 4 および 5 を繰り返します。
- 列の構成が完了したら、アプリケーションを実行し、選択した列のヘッダーにドロップダウン ボタンが表示されることを確認します。
図 2 は、列の種類を DataGridViewAutoFilterTextBoxColumn に設定するために使用されている [列の編集]
ダイアログ ボックスの例を示しています。
図 2. ColumnType プロパティが DataGridViewAutoFilterTextBoxColumn に設定されている [列の編集] ダイアログ ボックス
プログラムによってオートフィルタ機能をアプリケーションに追加するには
- Windows アプリケーション プロジェクトで、DataGridViewAutoFilter.dll アセンブリへの参照を追加します。
- 次の using
文をコード ファイルの先頭に追加して、
DataGridViewAutoFilter クラス名を修飾しなくても済むようにします。
using DataGridViewAutoFilter;
- DataGridView
コントロールをフォームに追加します。
- デザイナを使用するか、以下に示すイベントフックアップ コードを使用して、DataGridView.BindingContextChanged
イベントを処理します。イベント ハンドラは、DataSource
プロパティを設定する前にイベントに関連付けて、データ バインドの結果としてイベントが発生するようにする必要があります。
// このコードをコンストラクタに追加します。
dataGridView1.BindingContextChanged += new
EventHandler(dataGridView1_BindingContextChanged);
- DataGridView.DataSource
プロパティを設定します。以下のコードでは、デザイナを使用するか、コード内で、
customersBindingSource という名前の BindingSource
コンポーネントを既に作成し、初期化していることが前提となっています。プログラムによるデータ バインドの設定の詳細については、MSDN2 ライブラリの「方法 : データを Windows フォーム DataGridView コントロールにバインドする」を参照してください。
// このコードをフォーム読み込みイベント ハンドラに追加します。
dataGridView1.DataSource = customersBindingSource;
- BindingContextChanged
イベント ハンドラで、対象となる列の DataGridViewColumn.HeaderCell
プロパティを設定します。以下のサンプル コードでは、DataGridView
コントロール内の列を繰り返し処理し、各ヘッダーを新しい
DataGridViewAutoFilterColumnHeaderCell オブジェクトに設定します。
private void dataGridView1_BindingContextChanged(object sender, EventArgs e)
{
if (dataGridView1.DataSource == null) return;
foreach (DataGridViewColumn col in dataGridView1.Columns)
{
col.HeaderCell = new
DataGridViewAutoFilterColumnHeaderCell(col.HeaderCell);
}
dataGridView1.AutoResizeColumns();
}
- アプリケーションを実行し、選択した列のヘッダーにドロップダウン ボタンが表示されることを確認します。
フィルタ ステータスと [すべて表示] オプションを表示する StatusStrip コントロールを追加するには
- 前述の手順のいずれかに従って、オートフィルタ機能を DataGridView
コントロールに追加します。
- 次の using
文をコード ファイルの先頭にまだ追加していない場合は、ここで追加し、
DataGridViewAutoFilter クラス名を修飾しなくても済むようにします。
using DataGridViewAutoFilter;
- StatusStrip
コントロールを、
statusStrip1 という名前を持ち、filterStatusLabel および showAllLabel という 2 つの ToolStripStatusLabel
コンポーネントを含んでいるフォームに追加します。
- 以下のコードを使用するか、デザイナの [プロパティ]
ウィンドウの対応する設定を使用して、ラベルを構成します。
filterStatusLabel.Text = "";
filterStatusLabel.Visible = false;
showAllLabel.Text = "Show &All";
showAllLabel.Visible = false;
showAllLabel.IsLink = true;
showAllLabel.LinkBehavior = LinkBehavior.HoverUnderline;
-
showAllLabel コンポーネントの Click
イベントを処理します。
// このコードをコンストラクタに追加するか、
// デザイナを使用してハンドラをイベントに関連付けます。
showAllLabel.Click += new EventHandler(showAllLabel_Click);
// ...
private void showAllLabel_Click(object sender, EventArgs e)
{
DataGridViewAutoFilterTextBoxColumn.RemoveFilter(dataGridView1);
}
- DataGridView.DataBindingComplete イベントを処理します。
// このコードをコンストラクタに追加するか、
// デザイナを使用してハンドラをイベントに関連付けます。
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;
}
}
- アプリケーションを実行し、フィルタを適用します。
図 3 は、フィルタを適用した結果の StatusStrip
のサンプルを示しています。
図 3. フィルタ ステータスと [すべて表示] オプションを表示している StatusStrip コントロール
キーボードを使用してユーザーがドロップダウン リストを表示できるようにするには
- 次の using
文をコード ファイルの先頭にまだ追加していない場合は、ここで追加し、
DataGridViewAutoFilter クラス名を修飾しなくても済むようにします。
using DataGridViewAutoFilter;
- DataGridView.KeyDown イベントを処理します。
// このコードをコンストラクタに追加します。
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;
}
}
}
- アプリケーションを実行し、Alt + 上方向キーまたは Alt + 下方向キーを押して、現在の列のドロップダウン リストを開きます。Esc キーを押して、ドロップダウン リストを閉じます。
DataGridViewAutoFilterColumnHeaderCell クラスの実装に関する詳細
DataGridViewAutoFilter ライブラリの使用方法とその機能について理解できたので、この記事の残りの部分では、DataGridViewAutoFilter ライブラリがどのように動作するかについて詳しく説明します。
DataGridViewAutoFilter ライブラリは、そのままでもユーザーの要件を満たす場合があります。その場合、ユーザーが実装の詳細について理解する必要はありません。ただし、ニーズをさらに満たすためにこのライブラリの機能をカスタマイズまたは拡張する必要がある場合や、修正する必要のあるバグが見つかる場合があります。また、実装について理解しておくと、DataGridView
コントロール用に他の種類のカスタム ヘッダー セルを作成する場合に役立ちます。
DataGridViewAutoFilter ライブラリのプライマリ クラスは DataGridViewAutoFilterColumnHeaderCell クラスです。このクラスは DataGridViewColumnHeaderCell
クラスから派生します。
DataGridViewAutoFilterColumnHeaderCell の実装に関する詳細は、次の 4 つのカテゴリに分類できます。
- 初期化
- ドロップダウン ボタンの表示
- ドロップダウン リストを使用したユーザー操作の表示、非表示、および処理
- ユーザーが一覧からフィルタを選択したときの、バインドされたデータのフィルタ処理
以下のセクションでは、これらのカテゴリについて詳しく説明し、さまざまな点を示したサンプル コードを示します。サンプル コードは、付属の DataGridViewAutoFilter ライブラリから抜粋したものですが、簡潔にするために一部の詳細が省略されています。完全な詳細については、サンプルに含まれている完全なソース コードを参照してください。
初期化
主な初期化タスクは、ドロップダウン リストとして使用する ListBox
コントロールを作成することです。DataGridViewAutoFilter ライブラリでは、アプリケーション内のすべてのオートフィルタ ヘッダー セルに対し、ListBox
から派生した 1 つのコントロールを使用して、dropDownListBox という静的変数にこのコントロールを格納します。単一のインスタンスを使用できる理由は、一度に表示するのが 1 つのドロップダウン リストのみであるためです。また、ドロップダウン リストの内容、サイズ、および場所は、リストの表示時に決定するのが最も簡単なためでもあります。これらの要素は、フィルタ処理、サイズ変更、スクロールなどの、DataGridView
コントロールに対する頻繁な変更によって影響を受けるためです。
Windows フォームの ListBox
コントロールは、Excel オートフィルタ リストの外観と動作を指定するために使用されます。また、長いリスト内を移動するための垂直スクロール バーも用意されています。一方、ToolStripDropDown
コントロールが使用されない理由は、垂直スクロール バーがなく、メニューの通常の使用に見られるように、代わりに上方向ボタンと下方向ボタンが使用されるためです。ComboBox
コントロールが使用されない理由は、コントロールのテキスト ボックス部分を表示せずに、そのドロップダウン リストを簡単に表示することができないためです。
FilterListBox クラス
FilterListBox コントロールは、ListBox
コントロールを拡張して、親の DataGridView
コントロールが通常取得するキーボード メッセージを受信できるようにします。さらに、FilterListBox クラスでは一部の ListBox
プロパティ設定を変更して、コントロールをドロップダウン リストとして使用するように構成します。
FilterListBox クラスは、保護されている IsInputKey
および ProcessKeyMessage
メソッドをオーバーライドして、Alt + F4 などの、オペレーティング システムが処理するもの以外のすべてのキーストロークを取得できるようにします。Control.ProcessKeyMessage
メソッドは、通常はコントロールの親の ProcessKeyPreview
メソッドにメッセージをディスパッチします。コントロールの親がそのメッセージを必要としない場合、メッセージはコントロール独自の ProcessKeyEventArgs
メソッドに送られ、そこでキーボード イベントが生成されます。
不便なことに、DataGridView.ProcessKeyPreview
メソッドは、すべてのキーボード メッセージを取得します。ただし、編集モード中に通常の (ヘッダー以外の) セルによってホストされる編集コントロールに対するメッセージは例外です (編集コントロールのホストの詳細については、MSDN2 ライブラリの「方法 : Windows フォーム DataGridView Cells でコントロールをホストする」を参照してください)。キーボードの処理では、DataGridView
コントロールはその他すべての子コントロールを無視します。その結果、ホストされているコントロールによって処理されるものとユーザーが予期したキーストロークは、代わりに DataGridView
コントロールによって処理されます。
この動作を回避するために、FilterListBox クラスの ProcessKeyMessage
オーバーライドは親コントロールの処理を行わず、代わりにキーボード メッセージを ProcessKeyEventArgs
メソッドに直接送信します。ProcessKeyEventArgs
メソッドは次に、DataGridViewAutoFilterColumnHeaderCell クラスが処理できる FilterListBox キーボード イベントを起動します。
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
インスタンスを受け取るコンストラクタ オーバーロードを提供します。コンストラクタ オーバーロードは、指定されているセルのプロパティ値を新しいインスタンスにコピーします。これは、既存のヘッダー セルをオートフィルタ セルに置換する一方で、他のすべてのヘッダーの詳細はそのまま保持する必要がある場合に役立ちます。
Clone
メソッドも、コンストラクタ オーバーロードを使用して、既存のインスタンスと同じプロパティ値を持つ新しいインスタンスを作成します。このため、コンストラクタ オーバーロードでは、指定されている列ヘッダー セルがオートフィルタ セルであって、オートフィルタ固有のプロパティ値をコピーすることが可能であるかを判断します。
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 メソッド
オートフィルタ ヘッダー セルは、それが作成されたときではなく、DataGridView
コントロールに含まれている列に追加されたときに初めて、DataGridView
コントロールの一部になります。これが発生すると、OnDataGridViewChanged
メソッドが呼び出されます。DataGridViewAutoFilterColumnHeaderCell クラスはこのメソッドをオーバーライドして、DataGridView
コントロールが使用できるようになるまで実行できない初期化を行います。
オートフィルタ ヘッダー セルが、セル自体に含まれている 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();
}
ドロップダウン ボタンの表示
ドロップダウン ボタンは実際の Button
コントロールではありません。マウスのクリックを処理するためにオーバーライド可能な OnMouseDown
メソッドが DataGridViewCell
タイプによって提供されるので、Button
コントロールは必要ありません。オートフィルタ セルで必要な操作は、ボタンを描画し、ヘッダーの適切な部分でクリックが行われたことを確認することだけです。
Paint メソッド
Paint
メソッドは、ヘッダー セルにドロップダウン ボタンを描画します。ドロップダウン ボタンの外観は、次のようないくつかの要素の影響を受けます。
- ドロップダウン ボタンのサイズは、ヘッダー テキストの高さに基づきます。
- ドロップダウン ボタンの場所は、ヘッダー セルの場所と DataGridView.RightToLeft
プロパティの値に基づきます。
- アプリケーションに現在テーマが付いている場合は、ドロップダウン ボタンにもテーマが付きます。
-
dropDownListBox が現在表示されている場合は、ドロップダウン ボタンも押された状態で表示されます。
- 列が現在フィルタ処理されている場合は、ドロップダウン ボタンが強調表示されます。
- セルの
FilteringEnabled プロパティが false
の場合、ドロップダウン ボタンは表示されません。
これらの要素はアプリケーションの実行中に変化するので、これらの要素のほとんどは、Paint
メソッドが呼び出されたときに決定する必要があります。ただし、サイズと場所は比較的一定しているので、これらの値は DropDownButtonBounds プロパティに格納し、変更されない限り再利用できます。また、DropDownButtonBounds プロパティは、ユーザーがヘッダー セルをクリックしてドロップダウン リストを表示できる領域を示します。
ボタンのサイズと場所は、そのヘッダー セルのサイズまたは場所が変更されたときに、変更する必要があります。これは、DataGridView
コントロールの水平方向のスクロール、その列または列ヘッダーのサイズ変更、コントロール自体のサイズ変更 (たとえば、ドックが挿入されている DataGridView
コントロールを含むフォームのサイズ変更) などのユーザー操作によって発生します。前述のように、OnDataGridViewChanged
メソッドは、ハンドラをさまざまな DataGridView
イベントにアタッチします。スクロールやサイズ変更に関連するイベントのハンドラは、InvalidateDropDownButtonBounds メソッドを呼び出します。このメソッドでは、単にボタンのバインドを Rectangle.Empty
に設定します。
Paint
メソッドが呼び出されたときに、DropDownButtonBounds の値が Empty
の場合は、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)
{
// ベース メソッドを使用して既定の外観を表示します。
base.Paint(graphics, clipBounds, cellBounds, rowIndex,
cellState, value, formattedValue,
errorText, cellStyle, advancedBorderStyle, paintParts);
// フィルタが有効になっており、ペイント要求に ContentBackground が
// 含まれている場合にのみ続行します。
if (!FilteringEnabled ||
(paintParts & DataGridViewPaintParts.ContentBackground) == 0)
{
return;
}
// 現在のボタン範囲を取得します。
Rectangle buttonBounds = DropDownButtonBounds;
// フィルタ一覧が表示されているかどうかと、現在の列で
// フィルタが有効かどうかに応じて適切な状態を使用し、
// 手動で、またはビジュアル スタイルが有効な場合は
// ビジュアル スタイルを使用してボタンを表示します。
if (Application.RenderWithVisualStyles)
{
ComboBoxState state = ComboBoxState.Normal;
if (dropDownListBoxShowing)
{
state = ComboBoxState.Pressed;
}
else if (filtered)
{
state = ComboBoxState.Hot;
}
ComboBoxRenderer.DrawDropDownButton(
graphics, buttonBounds, state);
}
else
{
// ボタンを適切に表示して下矢印をオフセットするために、
// 押された状態を判断します。
Int32 pressedOffset = 0;
PushButtonState state = PushButtonState.Normal;
if (dropDownListBoxShowing)
{
state = PushButtonState.Pressed;
pressedOffset = 1;
}
ButtonRenderer.DrawButton(graphics, buttonBounds, state);
// 列に有効なフィルタがある場合は、塗りつぶしなしの
// 三角形として下矢印を表示します。有効なフィルタが
// ない場合は、塗りつぶされた三角形として下矢印を表示します。
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
メソッドをオーバーライドする必要がなくなります。
SetDropDownButtonBounds メソッドと AdjustPadding メソッドのソース コードは次のとおりです。
private void SetDropDownButtonBounds()
{
// セル表示用の四角形を取得します。この四角形を使用して
// ドロップダウン ボタンの位置を設定します。
Rectangle cellBounds =
this.DataGridView.GetCellDisplayRectangle(
this.ColumnIndex, -1, false);
// 変数を初期化してボタンの一辺の長さを保存します。
// 初期値はフォントの高さに基づいて設定します。
Int32 buttonEdgeLength = this.InheritedStyle.Font.Height + 5;
// セルの境界と埋め込みを計算します。
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;
}
// ボタンの一辺の長さを、列ヘッダの高さから境界および
// 埋め込みの高さを差し引いた長さに制限します。
if (buttonEdgeLength >
this.DataGridView.ColumnHeadersHeight -
borderAndPaddingHeight)
{
buttonEdgeLength =
this.DataGridView.ColumnHeadersHeight -
borderAndPaddingHeight;
}
// ビジュアル スタイルが有効かどうかに基づいた調整を行い、
// ドロップダウン ボタンの位置を計算します。
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;
}
// 計算した値を使用して dropDownButtonBoundsValue 値を設定し、
// それに応じてセルの埋め込みを調整します。
dropDownButtonBoundsValue = new Rectangle(left, top,
buttonEdgeLength, buttonEdgeLength);
AdjustPadding(buttonEdgeLength + leftOffset);
}
private void AdjustPadding(Int32 newDropDownButtonPaddingOffset)
{
// 新しい埋め込み調整と現在の埋め込み調整の
// 差を調べます。
Int32 widthChange = newDropDownButtonPaddingOffset -
currentDropDownButtonPaddingOffset;
// 埋め込みの変更が必要な場合は、新しい値を
// 保存して変更を加えます。
if (widthChange != 0)
{
// クライアントが埋め込みを追加できるように、ドロップダウン ボタンの
// オフセットを埋め込みとは別に保存します。
currentDropDownButtonPaddingOffset =
newDropDownButtonPaddingOffset;
// 調整量を使用して新しい埋め込みを作成し、セルの既存の
// Style.Padding プロパティ値に加算します。
Padding dropDownPadding = new Padding(0, 0, widthChange, 0);
this.Style.Padding = Padding.Add(
this.InheritedStyle.Padding, dropDownPadding);
}
}
ドロップダウン フィルタ リストを使用したユーザー操作の表示、非表示、および処理
ドロップダウン リストは通常、ユーザーがドロップダウン ボタンをクリックしたときに表示されます。OnMouseDown
メソッドは、ユーザーによる列ヘッダーのクリックを処理します。ユーザーが列ヘッダーをクリックしたときに、そのクリックが DropDownButtonBounds プロパティに指定されている境界の外で行われ、自動並べ替えが有効になっている場合は、列が並べ替えられ、並べ替えグリフがドロップダウン ボタンの横に表示されます。マウスが DropDownButtonBounds の指定境界内でクリックされ、ドロップダウン リストがまだ表示されていない場合には、ShowDropDownList メソッドを使用して表示されます。
前述のように、ユーザーが Alt + 下方向キーなどの特定のキーの組み合わせを押したときに、ドロップダウン リストが表示されるように DataGridView.KeyDown イベントを処理することもできます。正しいキーストロークが検出されると、イベント ハンドラは ShowDropDownList メソッドを呼び出して、ドロップダウン リストを表示します。
ドロップダウン リストが表示されると、ユーザーはキーボードを使用したリスト内の移動、スクロール バーが表示されている場合にはリストのスクロール、キーボードまたはマウスを使用した値の選択、または別の場所のクリックができるようになります。
フィルタ オプションをクリックするか、オプションを選択して Enter キーを押すと、UpdateFilter メソッド (以下のセクションで後述) に続いて HideDropDownList メソッドが呼び出されます。ドロップダウン リスト以外の場所をクリックし、ドロップダウン リストから入力フォーカスが外れると、HideDropDownList が呼び出され、イベント ハンドラがその dropDownListBox イベントから削除されます。さらに、リスト コントロールが非表示になり、DataGridView
コントロールから削除されます。
ユーザーが Esc キーを押すか、ドロップダウン リストの場所または内容を変更する DataGridView イベントが発生した場合には、HideDropDownList メソッドも呼び出されます。
特定の DataGridView
イベント ハンドラでは、ドロップダウン リストを非表示にする必要があります。ToolStrip
関連のコントロールなどの一部の UI 要素は、入力フォーカスを受け取らないためです。ユーザーがこのようなコントロールを操作した結果、DataGridView
コントロールまたはその内容が変更された場合は、ドロップダウン リストを表示し続けることが不適切なことがあります。たとえば、前述したような StatusStrip
コントロールの "すべて表示" リンクをユーザーがクリックした場合、データ ソースはフィルタ処理されなくなります。この場合、ドロップダウン リストがまだ表示されていると、以前のフィルタ設定に基づく誤ったフィルタ オプションが含まれている場合があります。また、フィルタの変更によって複数の列のサイズが変更された場合は、誤った場所に表示される場合があります。
ShowDropDownList メソッド
ドロップダウン リストの表示の詳細は、ShowDropDownList メソッドに含まれています。このメソッドは、次の操作を実行します。
-
dropDownListBox.Items コレクションにフィルタ オプションを設定します。主なタスクは、値をデータ ソースから取得することです。このタスクは、以下のセクションで後述するように、PopulateFilters メソッドによって処理されます。フィルタ値が取得されると、ShowDropDownList メソッドによってこれらの値が Items
コレクションに追加され、列に有効な値が存在する場合には、現在のフィルタ値が強調表示されます。
-
dropDownListBox の内容および他のいくつかの要素に基づいて、dropDownListBox.Bounds プロパティを設定します。この操作は、以下のセクションで後述するように、SetDropDownListBoxBounds メソッドによって処理されます。
- イベント ハンドラを
dropDownListBox イベントに関連付けて、ユーザーによるドロップダウン リストの操作を管理します。
- 新たに構成された
dropDownListBox を DataGridView
コントロールに表示します。
ドロップダウン リストが表示されると、ユーザーは前述のように直ちにこのリストを操作できるようになります。
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();
// セルを無効にして、ドロップダウン ボタンを
// 押された状態で再表示します。
this.DataGridView.InvalidateCell(this);
}
PopulateFilters メソッド
特定の列のドロップダウン フィルタ リストには、列に表示される各値の 1 つのコピーを含める必要があります。これは、列が現在フィルタを適用していないことが前提となっています。列がフィルタ処理されている場合は、フィルタを無視して、フィルタ処理されていない場合とリストの値が同じになるようにする必要があります。フィルタ リストには、列独自のフィルタにかかわらず、列に表示可能な値のみが含まれるので、その他すべての列のフィルタ値は、列独自のフィルタ値が有効でない場合でも、有効になります。
dropDownFilterList に表示される値は、DataGridView
コントロールに表示する場合と同様に、書式設定する必要があります。ただし、フィルタ値が選択されていると、その書式設定された値は BindingSource.Filter
プロパティと互換性を持たない場合があります。このため、書式設定された値と書式設定されていない値の両方の文字列表記が、filters と呼ばれる OrderedDictionary
インスタンスに格納されます。OrderedDictionary
を使用すると、辞書の Keys
コレクションを使用して、書式設定された値に正しい順序でアクセスすることにより、ShowDropDownList メソッドで dropDownListBox を設定できるようになります。フィルタ値が選択されている場合、UpdateFilter メソッドでは書式設定されている表示値を辞書のキーとして使用して、BindingSource.Filters
プロパティと互換性のある、書式設定されていない値を取得できます。
必要なフィルタ値を取得するために、PopulateFilters メソッドは次の操作を実行します。
- BindingSource.RaiseListChangedEvents
プロパティを false
に設定します。これにより
PopulateFilters メソッドは、DataGridView
によって表示が更新されることなく、現在の BindingSource.Filter
プロパティ値を変更できるようになります。
- 現在の Filter
値をキャッシュします。
-
FilterWithoutCurrentColumn メソッドを呼び出します。このメソッドは、現在の列に関連する部分を取り除くことで、現在のフィルタ文字列を解析します。
- Filter
プロパティを、解析された値に設定します。
- フィルタ辞書をクリアします。
- BindingSource
に含まれている各行の現在の列の値を取得して、ArrayList
に追加します。Null
と DBNull.Value
の値は除外されますが、その存在は後の処理のために記録されます。
- ArrayList
を並べ替えます。ArrayList.Sort
メソッドでは値の型の IComparable
実装を使用するので、文字列はアルファベット順に並べ替えられ、数値は数値順に並べ替えられ、DateTime
の値はカレンダー順に並べ替えられます。
- 並べ替えられた ArrayList
の各値に対し、DataGridViewCell.GetFormattedValue
メソッドを呼び出して列の InheritedStyle
プロパティ値に渡すことで、書式設定された文字列表記を判断します。これにより、DataGridView
セルと同じ書式設定を使用して、値がドロップダウン リストに表示されます。
- 書式設定された各値と、書式設定されていない各値の文字列表記がフィルタ辞書にまだ追加されていない場合には、追加します。この際、空の文字列は除外されますが、その存在は記録されます。
- 特殊なフィルタ オプションを、次のようにフィルタ辞書に追加します。
- リスト内の最初の項目として、常に (すべて) を追加します。
- 列に空の文字列 (または Null) および空でない文字列の両方が含まれている場合は、リストの最後に (空白セル) と (空白以外のセル) を追加します。
これらの特殊なオプションには、書式設定されていない文字列表記は必要ないので、これらは Null 値と共に辞書に追加されます。
- BindingSource.Filter
プロパティを、キャッシュされた値に復元します。
- BindingSource.RaiseListChangedEvents
プロパティを true
に設定し、通常の操作を再開します。
PopulateFilters メソッドのソース コードは次のとおりです。
private void PopulateFilters()
{
if (this.DataGridView == null) return;
// データ ソースを BindingSource にキャストします。
BindingSource data = this.DataGridView.DataSource as BindingSource;
// データ ソースによって DataGridView に変更が通知されないようにします。
data.RaiseListChangedEvents = false;
// 現在の BindingSource.Filter 値をキャッシュし、
// Filter プロパティを変更して現在の列の
// フィルタを一時的に削除します。
String oldFilter = data.Filter;
data.Filter = FilterWithoutCurrentColumn(oldFilter);
// フィルタ辞書をリセットし、一部のフラグを初期化して、
// 特殊なフィルタ オプションが必要かどうかを追跡します。
filters.Clear();
Boolean containsBlanks = false;
Boolean containsNonBlanks = false;
// ArrayList を初期化して、値を元の型で保存します。
// これにより、値を適切に並べ替えることができます。
ArrayList list = new ArrayList(data.Count);
// 各値を取得し、その値がまだ存在しない場合は
// ArrayList に追加します。
foreach (Object item in data)
{
Object value = null;
// 可能であれば ICustomTypeDescriptor インターフェイスを使用して
// 値を取得します。このインターフェイスは、プロパティとして
// 公開する値をカスタマイズする際に役立ちます。
// たとえば、DataRowView クラスは ICustomTypeDescriptor を
// 実装して、セル値をプロパティ値として公開します。
ICustomTypeDescriptor ictd = item as ICustomTypeDescriptor;
if (ictd != null)
{
value = ictd
.GetProperties()[this.OwningColumn.DataPropertyName]
.GetValue(item);
}
else
{
value = item.GetType()
.GetProperty(this.OwningColumn.DataPropertyName)
.GetValue(item, null /*property index*/);
}
// 空の値はスキップしますが、そのような値が存在することに注意してください。
if (value == null || value == DBNull.Value)
{
containsBlanks = true;
continue;
}
// 値がまだ存在しない場合は、その値を ArrayList に追加します。
if (!list.Contains(value))
{
list.Add(value);
}
}
// ArrayList を並べ替えます。既定の Sort メソッドでは、
// 保存されている値の IComparable 実装が使用されるため、
// 文字列、数値、および日付の各値が適切に並べ替えられます。
list.Sort();
// ArrayList の各値を書式付き表記に変換し、
// 書式付きと書式なしの両方の文字列表記を
// フィルタ辞書に保存します。
foreach (Object value in list)
{
// dropDownListBox の形式が列のセルに使用する表示形式と
// 一致するように、セルの GetFormattedValue メソッドを
// 列の InheritedStyle プロパティと共に使用します。
String formattedValue = null;
DataGridViewCellStyle style = OwningColumn.InheritedStyle;
formattedValue = (String)GetFormattedValue(value, -1, ref style,
null, null, DataGridViewDataErrorContexts.Formatting);
if (String.IsNullOrEmpty(formattedValue))
{
// 空の値はスキップしますが、そのような値が存在することに注意してください。
containsBlanks = true;
}
else if (!filters.Contains(formattedValue))
{
// 空でない値が存在するかどうかに注意してください。
containsNonBlanks = true;
// 空でないすべての値について、
// 書式付きおよび書式なしの文字列表記を
// フィルタ辞書に追加します。
filters.Add(formattedValue, value.ToString());
}
}
// キャッシュされたフィルタ文字列にフィルタを復元し、
// データ ソースの変更通知を再び有効にします。
data.Filter = oldFilter;
data.RaiseListChangedEvents = true;
// 書式なし表記は不要なため、特殊な
// フィルタ オプションを NULL 値と共に
// フィルタ辞書に追加します。
filters.Insert(0, "(All)", null);
if (containsBlanks && containsNonBlanks)
{
filters.Add("(Blanks)", null);
filters.Add("(NonBlanks)", null);
}
}
SetDropDownListBoxBounds メソッド
SetDropDownListBoxBounds メソッドは、ドロップダウン リストのサイズと場所を初期化します。推奨されるサイズは、主に dropDownListBox の内容に依存します。この内容は、filters 辞書の Keys
コレクションに格納されている、書式設定された値です。SetDropDownListBoxBounds メソッドは、各フィルタ値に対してまず Graphics.MeasureString
メソッドを呼び出します。各値に対し、幅が以前のすべての値よりも広い場合には、その幅が格納され、高さがすべての値の累積合計高さに追加されます。この結果が、推奨されるサイズの判断に使用されます。
推奨される高さは、次の値のうち、最も小さい値になります。
- すべてのフィルタ値の累積高さ
-
DropDownListBoxMaxLines プロパティ値から計算された、ユーザー指定の最大高さ
- DataGridView
コントロールのクライアント領域の使用可能な高さ
推奨される幅は、最も幅広の値の幅に、スクロールバーの幅を足し (推奨される高さではすべての dropDownListBox 項目が表示されない場合)、さらに埋め込み用のわずかな値を足したものになります。
推奨される dropDownListBox の場所は、ドロップダウン ボタンの場所と DataGridView
コントロールの端に基づきます。リストの右端は、ドロップダウン ボタンの右端に揃えるのが最適です。RightToLeft
が有効になっている場合は、代わりにリストの左端がボタンの左端に揃えられます。ただし、リストをボタンに揃えると、DataGridView
コントロールの端と重なってしまう場合は、リストの重なっている端がコントロールの端にくるようにします。
dropDownListBox のサイズと場所を指定すると、ドロップダウン リストを表示できるようになります。
SetDropDownListBoxBounds メソッドのソース コードは次のとおりです。
private void SetDropDownListBoxBounds()
{
// 計算に使用する変数を宣言し、
// dropDownListBoxHeight を初期化して
// ListBox 境界を含めます。
Int32 dropDownListBoxHeight = 2;
Int32 currentWidth = 0;
Int32 dropDownListBoxWidth = 0;
Int32 dropDownListBoxLeft = 0;
// フィルタ辞書 Keys コレクション内の書式付きの各値について、
// その高さを dropDownListBoxHeight に加算し、幅が以前の
// すべての値よりも広い場合は、dropDownListBoxWidth をその幅に設定します。
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;
}
}
}
// 幅を大きくして、横の余白と境界を含めます。
dropDownListBoxWidth += 6;
// dropDownListBox の高さを DropDownListBoxMaxHeightInternal の
// 値に制限します。この値は DropDownListBoxMaxLines プロパティの
// 値に基づいていますが、DataGridView コントロールで使用可能な
// 最大の高さによって制限されます。
if (dropDownListBoxHeight > DropDownListBoxMaxHeightInternal)
{
dropDownListBoxHeight = DropDownListBoxMaxHeightInternal;
// 希望の高さが使用可能な高さを超える場合は、
// 幅を調整して縦のスクロール バーを含めます。
dropDownListBoxWidth += SystemInformation.VerticalScrollBarWidth;
}
// ドロップダウン ボタンの位置に基づき、
// RightToLeft プロパティ値を考慮して、
// dropDownListBox の左端の最適な位置を計算します。
if (this.DataGridView.RightToLeft == RightToLeft.No)
{
dropDownListBoxLeft = DropDownButtonBounds.Right -
dropDownListBoxWidth + 1;
}
else
{
dropDownListBoxLeft = DropDownButtonBounds.Left - 1;
}
// DataGridView コントロールの使用可能な
// 横幅の左端および右端を調べます。
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;
}
}
// DataGridView の左端または右端と重なる場合は、
// dropDownListBox の位置や幅を調整します。
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;
}
}
}
// 計算した値を使用して ListBox.Bounds プロパティを設定します。
dropDownListBox.Bounds = new Rectangle(dropDownListBoxLeft,
DropDownButtonBounds.Bottom, // top of drop-down list box
dropDownListBoxWidth, dropDownListBoxHeight);
}
ListBox イベントを処理する
処理の必要な dropDownListBox イベントは、LostFocus
、MouseClick
、および KeyDown
イベントです。ユーザーがドロップダウン リスト以外の場所をクリックしたために、ドロップダウン リストから入力フォーカスが外れた場合、LostFocus
イベント ハンドラは HideDropDownList メソッドを呼び出します。ユーザーがフィルタ オプションをクリックするか、Enter キーを押すと、MouseClick
または KeyDown イベント ハンドラによって UpdateFilter メソッドが呼び出され、さらに HideDropDownList メソッドが呼び出されます。ユーザーが Esc キーを押すと、KeyDown イベント ハンドラによって HideDropDownList メソッドのみが呼び出されます。
バインド データをフィルタ処理する
ユーザーが特定の列のドロップダウン リストからフィルタ オプションをクリックすると、データ ソースがフィルタ処理され、その列で選択されている値を持つ行のみが表示されます。データ ソース全体に対し、BindingSource.Filter
プロパティは 1 つのみ存在するので、選択されているフィルタ オプションをすべての列のフィルタ オプションと組み合わせて、1 つのフィルタ文字列を作成する必要があります。
フィルタ文字列を操作するためのメソッドは 4 つあります。これらは、UpdateFilter、FilterWithoutCurrentColumn、RemoveFilter、および GetFilterStatus です。
UpdateFilter メソッド
UpdateFilter メソッドは、ドロップダウン フィルタ リストでのユーザーの選択に応答し、BindingSource.Filter
値を変更します。これを行うために、次の操作が実行されます。
-
FilterWithoutCurrentColumn メソッドを呼び出して、現在の列のフィルタ値を含まない、解析済みフィルタ文字列を取得します。
- ユーザーが (すべて) のオプションを選択した場合は、解析された値に Filter プロパティを設定します。
- ユーザーが (すべて) 以外のオプションを選択した場合は、列のフィルタ文字列を作成して現在の Filter 値に追加します。
通常のフィルタ値のフィルタ文字列の形式は次のとおりです。
[列名]='フィルタ値'
(空白セル) および (空白以外のセル) オプションに対しては、フィルタ文字列により、Null 値の空の文字列を使用して、値が文字列に変換され、変換後の値の長さがゼロであるかどうかがテストされます。これらのフィルタ文字列の形式は次のとおりです。
LEN(ISNULL(CONVERT([列名],'System.String'),''))=0
LEN(ISNULL(CONVERT([列名],'System.String'),''))>0
BindingSource.Filter
プロパティ値が Null または空の場合は、UpdateFilter メソッドによってこの値が列のフィルタ文字列に設定されます。Filter
プロパティが Null または空以外の場合は、列のフィルタが Filter 値の最後に追加され、文字列 " AND " を含む既存の値と区切られます。
UpdateFilter メソッドのソース コードは次のとおりです。
private void UpdateFilter()
{
// 選択が変更された場合にのみ続行します。
if (dropDownListBox.SelectedItem.ToString()
.Equals(selectedFilterValue))
{
return;
}
// 新しい選択値を保存します。
selectedFilterValue = dropDownListBox.SelectedItem.ToString();
// データ ソースを IBindingListView にキャストします。
IBindingListView data =
this.DataGridView.DataSource as IBindingListView;
// ユーザーが [すべて] を選択した場合は、
// 列で現在有効なフィルタを削除します。
if (selectedFilterValue.Equals("(All)"))
{
data.Filter = FilterWithoutCurrentColumn(data.Filter);
filtered = false;
currentColumnFilter = String.Empty;
return;
}
// この列のフィルタ文字列を格納する変数を宣言します。
String newColumnFilter = null;
// 閉じ角括弧をバックスラッシュでエスケープして、列名を
// Filter プロパティに使用可能な形式で保存します。
String columnProperty =
OwningColumn.DataPropertyName.Replace("]", @"\]");
// ユーザーの選択に基づいて列フィルタ文字列を決定します。
// (Blanks) および (NonBlanks) の場合、フィルタ文字列によって列値が
// NULL になるか空の文字列になるかが決まります。それ以外の場合は、
// フィルタ文字列によって列値が選択した値になるかどうかが決まります。
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;
}
// BindingSource.Filter 値から以前の列フィルタ文字列を削除し、
// 必要に応じて " AND " を使用して新しい列フィルタ文字列を
// 追加することによって、新しいフィルタ文字列を決定します。
String newFilter = FilterWithoutCurrentColumn(data.Filter);
if (String.IsNullOrEmpty(newFilter))
{
newFilter += newColumnFilter;
}
else
{
newFilter += " AND " + newColumnFilter;
}
// フィルタを新しい値に設定します。
try
{
data.Filter = newFilter;
}
catch (InvalidExpressionException ex)
{
throw new NotSupportedException(
"Invalid expression: " + newFilter, ex);
}
// 列が現在フィルタ処理されていることを示し、
// 今後の FilterWithoutCurrentColumn メソッドの
// 呼び出しで使用する新しい列フィルタを保存します。
filtered = true;
currentColumnFilter = newColumnFilter;
}
FilterWithoutCurrentColumn メソッド
FilterWithoutCurrentColumn メソッドは、任意のフィルタ文字列を解析し、現在の列のフィルタを削除します。このメソッドは BindingSource.Filter
プロパティを直接変更しませんが、Filter
プロパティを変更するために UpdateFilter および PopulateFilters メソッドによって使用されます。
フィルタ文字列を解析するために、FilterWithoutCurrentColumn メソッドは currentColumnFilter フィールドを使用します。このフィールドには、BindingSource.Filter
プロパティ値の現在の列の部分のみが格納されます。FilterWithoutCurrentColumn メソッドは、currentColumnFilter 値に指定されているフィルタ文字列を検索します。列の値が見つかった場合は、列の値を含まずに、指定された文字列のコピーが返されます。戻り値が有効なフィルタ文字列にならないようになる異種の " AND " 区切り文字は使用されません。
FilterWithoutCurrentColumn メソッドのソース コードは次のとおりです。
private String FilterWithoutCurrentColumn(String filter)
{
// 有効なフィルタがない場合は、String.Empty を返します。
if (String.IsNullOrEmpty(filter))
{
return String.Empty;
}
// 列がフィルタ処理されていない場合は、未変更のフィルタ文字列を返します。
if (!filtered)
{
return filter;
}
if (filter.IndexOf(currentColumnFilter) > 0)
{
// 現在の列フィルタが最初のフィルタでない場合は、
// 現在の列フィルタがなく、前に "AND" が付かない
// 特定のフィルタ値を返します。
return filter.Replace(
" AND " + currentColumnFilter, String.Empty);
}
else
{
if (filter.Length > currentColumnFilter.Length)
{
// 現在の列フィルタが複数のフィルタの最初にある場合は、
// 現在の列フィルタがなく、後ろに "AND" が付かない
// 特定のフィルタ値を返します。
return filter.Replace(
currentColumnFilter + " AND ", String.Empty);
}
else
{
// 現在の列フィルタが唯一のフィルタである場合は、
// 空の文字列を返します。
return String.Empty;
}
}
}
RemoveFilter メソッドと GetFilterStatus メソッド
静的な RemoveFilter および GetFilterStatus メソッドは、クライアント アプリケーションがフィルタのステータス文字列と [すべて表示]
オプションを簡単に表示できるようにするために用意されています。これらのメソッドは、個別のセルではなく、データ ソースに関連しているため、"静的" です。
ただし、データ ソースはさまざまな DataGridView
コントロールごとに異なる場合があるので、これらのメソッドではクライアント アプリケーションは参照内で DataGridView
コントロールに渡す必要があります。これらのメソッドはセル クラスに実装されますが、便宜を図るために、DataGridViewAutoFilterTextBoxColumn クラスを使用しても公開されます。
RemoveFilter メソッドは、BindingSource.Filter
のプロパティ値を null
に設定してすべてのフィルタを削除します。このメソッドを使用すると、クライアント アプリケーションは Filter
プロパティに直接アクセスせずに、[すべて表示]
オプションを実装できるようになります。クライアント アプリケーションは、問題なく Filter
プロパティを null
または String.Empty
に設定できますが、他の値に設定すると、DataGridViewAutoFilter ライブラリによるフィルタ処理が妨げられる場合があります。この理由から、Filter
プロパティに対する変更は、クライアント コードに表示しないことをお勧めします。
GetFilterStatus メソッドは、BindingSource.Count
プロパティのフィルタ処理された値とフィルタ処理されていない値を含むステータス文字列を返します。このメソッドは、RemoveFilter メソッドよりも多くの処理を行います。Filter
プロパティ値を一時的に変更して、フィルタ処理されていない数を取得する必要があるためです。これは、PopulateFilters メソッドに関して説明した前述のプロセスと同じようにして行われます。データ ソースが現在フィルタ処理されていない場合、GetFilterStatus メソッドは空の文字列を返します。それ以外の場合は、次の形式の文字列を返します。
[フィルタ処理されていない数] of [フィルタ処理された数] records found
GetFilterStatus メソッドのソース コードは次のとおりです。
public static String GetFilterStatus(DataGridView dataGridView)
{
// 指定した値が有効な場合にのみ続行します。
if (dataGridView == null)
{
throw new ArgumentNullException("dataGridView");
}
// データ ソースを BindingSource にキャストします。
BindingSource data = dataGridView.DataSource as BindingSource;
// 適切なデータ ソースがないか、有効なフィルタがない場合は、
// String.Empty を返します。
if (String.IsNullOrEmpty(data.Filter) ||
data == null ||
data.DataSource == null ||
!data.SupportsFiltering)
{
return String.Empty;
}
// フィルタ処理後の行数を取得します。
Int32 currentRowCount = data.Count;
// データのフィルタ処理を一時的に解除して、
// フィルタ処理前の行数を取得します。
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");
// フィルタ処理後の数とフィルタ処理前の数が同じである場合は、
// String.Empty を返します。それ以外の場合は、ステータス文字列を返します。
if (currentRowCount == unfilteredRowCount)
{
return String.Empty;
}
return String.Format("{0} of {1} records found",
currentRowCount, unfilteredRowCount);
}
RemoveFilter および GetFilterStatus メソッドの詳細については、この記事で前述した「DataGridViewAutoFilter ライブラリを使用する」を参照してください。
その他のプロパティ
DataGridViewAutoFilterColumnHeaderCell クラスは、DataGridViewColumnHeaderCell
基本クラスから継承したクラスに 3 つのプロパティを追加します。これらのプロパティは、AutomaticSortingEnabled、FilteringEnabled、および DropDownListBoxMaxLines です。
AutomaticSortingEnabled プロパティを使用すると、列の SortMode
プロパティ値を Programmatic
にしたままで、自動並べ替えを無効にできます。オートフィルタ機能を使用した自動並べ替えでは、SortMode
プロパティ値を Programmatic
に設定し、ドロップダウン ボタンをクリックしても列が並べ替えられないようにする必要があります。ただし、ユーザー自身が並べ替えを処理する場合には、SortMode
プロパティを Programmatic
に設定されたままにして、並べ替えグリフの列ヘッダーにスペースを保持する必要があります。
FilteringEnabled プロパティを使用すると、フィルタ処理を無効にできます。FilteringEnabled が false
のとき、ドロップダウン ボタンは表示されず、ヘッダーの表示と動作は通常の列ヘッダー セルと同じようになります。
DropDownListBoxMaxLines プロパティを使用すると、ドロップダウン リストに表示される行の推奨最大数をカスタマイズできます。実際の高さは DataGridView
コントロールで使用可能な高さによって制約を受けますが、このプロパティでは高さをさらに制限できます。
これらのセルのプロパティは、便宜を図るために、DataGridViewAutoFilterTextBoxColumn クラスによっても公開されます。
DataGridViewAutoFilterTextBoxColumn クラスの実装に関する詳細
DataGridViewAutoFilterTextBoxColumn クラスは、自動フィルタのサポートを既存の列の種類に追加する方法の例を示すために用意されています。自動フィルタの列の種類は、この記事で前述したように、Windows フォーム デザイナを使用して、ユーザーがオートフィルタ機能をユーザーのアプリケーションに追加できるようにする場合に特に役立ちます。
DataGridViewAutoFilterTextBoxColumn クラスにおける重要なコードは、クラス コンストラクタです。このコンストラクタの唯一の機能は、DefaultHeaderCellType
プロパティを DataGridViewAutoFilterColumnHeaderCell の種類に設定し、SortMode
プロパティを Programmatic
に設定して、ドロップダウン ボタンがクリックされても自動並べ替えが行われないようにすることです。
public DataGridViewAutoFilterTextBoxColumn() : base()
{
base.DefaultHeaderCellType =
typeof(DataGridViewAutoFilterColumnHeaderCell);
base.SortMode = DataGridViewColumnSortMode.Programmatic;
}
DataGridViewAutoFilterTextBoxColumn クラスの残りのコードは、便宜を図るために示されています。DefaultHeaderCellType
および SortMode
プロパティは、これらのプロパティをデザイナで非表示にし、SortMode
プロパティが Automatic
に設定されている場合に例外をスローするために、再実装されます。さらに、列のクラスはセル クラスの新しいパブリック プロパティおよび静的メソッドを公開します。これらのメンバは、セル メンバをラップし、列のインスタンスでプロパティ値を設定したときに、列のヘッダー セル インスタンスの同じプロパティが更新されるようにします。
可能な機能拡張
DataGridViewAutoFilter ライブラリには、列のフィルタ処理用の基本的なユーザー インターフェイスが用意されており、DataGridView 列ヘッダー セルのカスタマイズ方法が示されています。また、既存のセルの種類からの派生が不可能な場合に、セル内で Windows フォーム コントロールをホストする方法も示されています。
オートフィルタ機能としての DataGridViewAutoFilter ライブラリには、強化できる可能性を持ついくつかの領域が残されています。次の一覧は、DataGridViewAutoFilterColumnHeaderCell クラスに対して考慮できる強化点を示しています。
- カスタム フィルタ: Excel に見られるようなカスタム フィルタ ダイアログ ボックスを表示すると、"を含む"、"を含まない"、"で始まる" などのフィルタ値を指定できます。
- 非バインド モードまたは仮想モードでのデータ ソースのフィルタ処理。
- 画像などの特殊なセル値のサポート。
- 並べ替える列とその並べ替え優先順位を示すヘッダー ラベルを含む、複数列の並べ替え機能などの、追加のヘッダー セル機能との統合。
その他のリソース
この記事および DataGridViewAutoFilter ライブラリに関するご意見、ご感想、および更新の有無のチェックについては、「Windows Forms Documentation Updates」ブログ (英語) を参照してください。
次のリソースには、DataGridView セルのカスタマイズに関する追加情報が記載されています。
一般的な Windows フォームの詳細については、以下を参照してください。