3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。(jehupc.exblog.jpから移転中)

(.Net)DataGridViewでカスタムクラスをバインドした状態でソートを行う。

(.Net)DataGridViewでカスタムクラスをバインドした状態でソートを行う。

DataGridViewで、カスタムクラス(独自クラス)のリストをバインドすると、ソートができません。

ソートグリフ(列ヘッダに表示される▽△マーク)も表示されません。

例えば下記のようになカスタムクラスのリストをDataGridViewにバインドするコードがあるとします。(C#)

(文字数制限のため今回はハイライトなしです。)

public partial class Form1 : Form{

private void Form1_Load(object sender, EventArgs e) {

//カスタムクラスのリスト生成

List lstCls = new List();

lstCls.Add(new TestClass2("001", "b"));

lstCls.Add(new TestClass2("002", "a"));

lstCls.Add(new TestClass2("003", "c"));

lstCls.Add(new TestClass2("004", "d"));

dataGridView1.DataSource = lstCls;

foreach (DataGridViewColumn item in dataGridView1.Columns) {

item.SortMode = DataGridViewColumnSortMode.Automatic;

}

}

}

public class TestClass2{

public string Str1 { get; set; }

public string Str2 { get; set; }

public TestClass2(string a,string b){

Str1 = a;

Str2 = b;

}

}

上記のコードで、バインドした後に、

dataGridView1.Sort( dataGridView1.Columns["Str1"] , ListSortDirection.Ascending);

というように、DataGridView.Sortメソッドを使ってソートを試みると、下記のような例外が発生します。

InvalidOperationException

Message="並び替えるには、IBindingList オブジェクトに DataGridView コントロールをバインドしなければなりません。"

じゃあ、ソートするにはどうすればいいんでしょう。

ということで、参考にさせてもらったのが、リフレクションを利用したソート(メモ)です。

Generic.Comparerクラスを継承して、ソート時の比較の条件を指定してやるといいようです。また、カスタムクラスのどのプロパティに対してソートするかをリフレクションを使って動的に設定できるので、便利です。(参考先さんに感謝ですね)

参考先sourceそのままですが、一応載せておきます。(C#)

/// グリッドにバインドしたUserリストを並べ替えるクラス(List.Sortで呼ばれるためのクラス)

public class TestClass2Comparer : System.Collections.Generic.Comparer{

private string mObject = string.Empty;

private bool _Ascending = true;

private SortOrder ord = SortOrder.Ascending;

/// 比較の対象とするプロパティの名称を取得、または設定します。

public string SortProperty {

get { return this.mObject; }

set {

// 対象のプロパティが登録されているか、確認する

Type chargeType = typeof(User);

System.Reflection.PropertyInfo[] info = chargeType.GetProperties();

for (int idx = 0; idx < info.Length; idx++) {

string propertyName = info[idx].Name;

if (propertyName.CompareTo(value) == 0) {

this.mObject = value;

return;

}

}

throw new ArgumentException("間違ってるよ");

}

}

/// ソート順

public SortOrder SortDirection {

get { return this.ord; }

set { this.ord = value; }

}

/// 比較メソッド。

public override int Compare(TestClass2 x, TestClass2 y) {

if (ord == SortOrder.Ascending || ord == SortOrder.None){

return CompareCore(x, y);

}

return CompareCore(y, x);

}

/// 比較するコアメソッド

private int CompareCore(TestClass2 x, TestClass2 y) {

System.Reflection.PropertyInfo xInfo = x.GetType().GetProperty(this.mObject);

System.Reflection.PropertyInfo yInfo = y.GetType().GetProperty(this.mObject);

if (xInfo == null || yInfo == null){

return (xInfo == null ? 1 : 0) - (yInfo == null ? 1 : 0);

}

object xObj = xInfo.GetValue(x, null);

object yObj = yInfo.GetValue(y, null);

//NULLを最小とする

if (xObj == null && yObj == null)

return 0;

if (xObj == null)

return -1;

if (yObj == null)

return 1;

IComparable comp = xObj as IComparable;

if (comp != null) {

//Str1のプロパティの場合、桁数が長い方を大きいとみなす時の例

//(つまり、ここでプロパティ毎の比較基準をカスタマイズできる)

if (mObject.Equals("Str1")){

string sx = xObj.ToString();

string sy = yObj.ToString();

int ix, iy;

//桁数が長い方を大きいとみなすよ

if (int.TryParse(x.Str1, out ix) && int.TryParse(y.Str1, out iy)) return ix - iy;

return String.Compare(x.Str1, y.Str1); //文字なら通常の比較

}

return comp.CompareTo(yObj) * (this._Ascending == true ? 1 : -1);

} else {

// 比較できない

return 0;

}

}

}

ソートの実行時は下記のようにすればOKです。

DataGridViewのデータソース(つまりカスタムクラスのリスト)にたいして、Sortメソッドの引数に上記の比較クラス(TestClass2Comparer)を渡してソートをしてもらい、DataGridViewをRefreshします。

//drgはDataGridView

//TestClass2のコレクションをソート

//ソートするために、TestClass2クラスの比較クラス生成

TestClass2Comparer cmp = new TestClass2Comparer();

DataGridViewColumn clm = drg.Columns[iColumnIndex];

//ソート順を決定

switch (clm.HeaderCell.SortGlyphDirection)

{

case SortOrder.Ascending:

cmp.SortDirection = SortOrder.Descending;

break;

case SortOrder.Descending:

case SortOrder.None:

default:

cmp.SortDirection = SortOrder.Ascending;

break;

}

//他の列のソートモードを解除

foreach (DataGridViewColumn item in drg.Columns)

{

item.HeaderCell.SortGlyphDirection = SortOrder.None;

}

clm.HeaderCell.SortGlyphDirection = cmp.SortDirection;

List lst = (List)drg.DataSource;

//ソート対象の列決定する

cmp.SortProperty = clm.DataPropertyName;

//ソートを実行する

lst.Sort(cmp);

//グリッド更新

drg.Refresh();