(.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();