タグ:DataGridView ( 30 ) タグの人気記事
(.Net)DataGridViewで右クリックしたときに行選択したい
DataGridViewでコンテキストメニューを割り当てた時、右クリックをしても、クリックしたセルに対してイベント走るのではなく、その時に選択されているセルに対してイベントが走ってしまいます。

Excelみたいに、右クリックしたら、マウスポインタの位置の行が選択されて、その後コンテキストメニューが出るようにしたい場合、以下のようにMouseDownイベントで行選択してしまえばいいようです。
参考先そのままですがコードを載せておきます。(C#)

/// <summary>
/// セルでマウスダウンイベントあった時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dgvParent_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
dgvParent.ClearSelection();
dgvParent.Rows[e.RowIndex].Selected = true;
}
 
}



参考:
DataGridView で右クリックして行を選択
[PR]
by Jehoshaphat | 2013-03-31 22:59 | .Net開発 | Trackback | Comments(0)
(.Net)DataGridViewでリセットする方法
DataGridViewにDataTableをバインドし、ユーザがリセットボタンを押したタイミングで、初期値に戻すための方法です。

DataGridView のメンバでそれらしいを探してみたんですが、見当たりません。
仕方なく下記のような方法をとることとしました。(C#)
DataTable mTblBkup;
DataTable tbl ; //tblには既にインスタンスが生成されてることとします。
 
public void Form1_Load(DataTable tbl)
{
mTblBkup = tbl.Copy();
 
datagridview1.DataSource = tbl;
 
}
 
private void btnReset_Click(object sender, EventArgs e)
{
tbl = mTblBkup.Copy();
dgvFilter.DataSource = tbl;
}

DataTable.Copy で DataTable のバックアップを作成し、リセットボタンを押下したら、バックアップを戻すというような方法です。
あまり、きれいな方法ではありませんが、とりあえずできました。
[PR]
by jehoshaphat | 2010-09-16 20:04 | .Net開発 | Trackback | Comments(1)
(.Net)DataGridViewセルスタイルの優先順位
最初 DataGridView でセルの値によって背景変更する処理をしていました。
例えばTrueなら背景色を付け、Falseならスタイルを無しする下記のような感じです。(C#)

//row は DataGridViewRow オブジェクト
if ((bool)row.Cells["Column1"].Value == true){
row.DefaultCellStyle.BackColor = Color.Red;
}else{
row.DefaultCellStyle.BackColor = SystemColors.WindowText;
}


で、それから下記のように列(Column1)の背景色を設定しようとしたができませんでした。

DataGridView1.Columns["Column1"].DefaultCellStyle.BackColor = Color.Honeydew;

どうやら、スタイルの設定には優先順位があるようです。

優先順位については、dobon:DataGridViewにセルスタイルを設定するで書かれていました。

つまり先にセルスタイルが優先されるので、この場合後から列スタイルを設定しても意味が無かったようです。

しかも今回 row.Cells["Column1"] が false ならセルスタイルを解除したいのになぜか SystemColors.WindowText を指定しているのも問題でした。
この場合、CellStyleにnullを指定してやればいいようです。
こうすると、row.Cells["Column1"] が false のときは、Color.Honeydew 背景色が Color.Honeydew となります。(下記コード参照)

//スタイルをnullにするとスタイル解除となる。
if ((bool)row.Cells["Column1"].Value == true) {
row.DefaultCellStyle.BackColor = Color.Red;
} else {
//row.DefaultCellStyle.BackColor = SystemColors.WindowText;
row.DefaultCellStyle = null;
}

[PR]
by jehoshaphat | 2010-06-04 00:34 | .Net開発 | Trackback | Comments(2)
(.Net)DataGridView でカスタムクラスバインド時にスタイル設定後ソートすると...
カスタムクラスの List をバインドした DataGridView があります。
セル値を変更してまだDBに保存する前の状態の場合にセルの背景色を変え、保存したら元の背景色に戻すという動作を実現したいと考えてます。

CellValidated イベントや CellValueChanged イベントでセルの背景色を変更しますが、問題がソートしたときです。
今回はカスタムクラスをソートするという特異な要件で、ソートするとデータは並び変わってもセルスタイルはソートされないんですよね。
(カスタムクラスのソートは(.Net)DataGridViewでカスタムクラスをバインドした状態でソートを行う参照)

CellValidatingでの背景色変更はこんな感じです。(C#)
private void dgvParent_CellValueChanged(object sender, DataGridViewCellEventArgs e){
if (mFormLoad) return;
dataGridView1[e.ColumnIndex, e.RowIndex].Style.BackColor = Color.Red;
}

セルの値に基づいてするときは、DataGridView.CellFormattingイベントが使えるのですが、今回はセルの内容ではないのでできません。
(CellFormattingについては、(.Net)バインドされたDataGridViewでソートするとセルの色が元に戻る参照。)
ちなみに、DataGridView に DataTableをバインドした場合は、ソートすると背景色消えるんですよね。

結局、バインドしているカスタムクラスの一意なキーとbool型を持ったグリッドの状態を持つ DataTable を作って、編集があれがグリッド状態保持 DataTable の任意の行をTrueにし、ソート後にループで回してスタイルを変更するというかなり面倒な手法を使うことにしました。。。

もっとスムーズな方法はないものですかね。。?
[PR]
by jehoshaphat | 2010-06-02 23:21 | .Net開発 | Trackback | Comments(0)
(.Net)DataGridViewRow作成時に列名指定で値を入れる

最近、DataGridViewRow.CreateCells()というメソッドを知りました。
これは既存の DataGridView の列定義に基づいてセル(正確にはセルテンプレート)を作成してくれるもので、新規DataGridViewの行を作るときに便利そうです。

それで、下記のようなコードを書いてみました。新たな DataGridViewRow を作成し、DataGridView に基づくセルテンプレート作成後、セルに値を入れグリッドにバインドするような流れです。(C#)
//dgvTest は DataGridViewのインスタンス
 
DataGridViewRow row = new DataGridViewRow();
row.CreateCells(dgvTest);
//列名指定で、セルに値代入
row.Cells["ColumnA"].Value = "a";
dgvInputPayList.Rows.Add(row);

上記のようにすると下記のような例外となりました。

System.ArgumentException はハンドルされませんでした。
Message="ColumnA という列は見つかりません。\r\nパラメータ名: columnName"
Source="System.Windows.Forms"
ParamName="columnName"

ただ、列名ではなく列のインデックスを指定すると可能なようです。
row.Cells[0].Value = "a";


で、とあるサイトを参考にすると、DataGridView に行を追加した後だとできるようです。
//dgvTest は DataGridViewのインスタンス
DataGridViewRow row = new DataGridViewRow();
row.CreateCells(dgvTest);
dgvInputPayList.Rows.Add(row);
row.Cells["ColumnA"].Value = "a";


しかし、上記だと2行目からがNGになります。
//dgvTest は DataGridViewのインスタンス
DataGridViewRow row = new DataGridViewRow();
row.CreateCells(dgvTest);
dgvInputPayList.Rows.Add(row);
row.Cells["ColumnA"].Value = "a";
 
DataGridViewRow row2 = new DataGridViewRow();
row2.CreateCells(dgvTest);
dgvInputPayList.Rows.Add(row2);
row2.Cells["ColumnA"].Value = "b";


System.ArgumentOutOfRangeException はハンドルされませんでした。
Message="指定された引数は、有効な値の範囲内にありません。\r\nパラメータ名: rowIndex"
Source="System.Windows.Forms"
ParamName="rowIndex"

どうやら、行のインデックスがまずいらしいです。


結局行を追加した後に、DataGridViewの行に対して、値を指定するようにしました。
//dgvTest は DataGridViewのインスタンス
DataGridViewRow row = new DataGridViewRow();
row.CreateCells(dgvTest);
int i = dgvInputPayList.Rows.Add(row);
dgvInputPayList["ColumnA", i].Value = "a";


参考:
DataGridViewコントロールを操作する101の方法 / DevX - japan.internet.com コラム
[PR]
by jehoshaphat | 2010-05-02 13:04 | .Net開発 | Trackback | Comments(4)
(.Net)カスタムクラスをDataGridViewにバインドしたときに、選択したレコードのオブジェクトを取得する
List<T>を DataGridView にバインドしてるんですが、ユーザーが行を選択した時に選択された行に対応するオブジェクト取得する方法です。
.Net にはそのためのプロパティが用意されていました。
それは、DataGridViewRow.DataBoundItemプロパティです。
詳しくはMSDN:方法 : Windows フォームの DataGridView 行にバインドされたオブジェクトにアクセスするが参考になります。

ただ、行選択モード(DataGridViewSelectionMode)が FullRowSelect にしといたほうが安全ですね。
セルだけ選択した場合は取得できないようなので。。。

とりあえず、サンプルです。(C#)

public partial class Form1 : Form{
List<user> mlst = new List<user>();
 
private void Form1_Load(object sender, EventArgs e) {
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
mlst.Add(new user("hogehoge1", "1"));
mlst.Add(new user("hogehoge2", "2"));
mlst.Add(new user("hogehoge3", "3"));
 
dataGridView1.DataSource = null;
dataGridView1.DataSource = mlst;
}
 
private void button3_Click(object sender, EventArgs e) {
foreach (DataGridViewRow row in dataGridView1.SelectedRows) {
//選択したレコードに対応するオブジェクトを取得
user u =(user)row.DataBoundItem;
Console.WriteLine(u.name + " , " + u.id);
}
Console.WriteLine();
}
}
 
//カスタムクラス(エンティティ)
public class user{
public string name { get; set; }
public string id { get; set; }
public user(string sname, string sid) {
name = sname;
id = sid;
}
}

[PR]
by jehoshaphat | 2010-04-29 23:08 | .Net開発 | Trackback | Comments(0)
(.Net)DataGridViewへのカスタムクラスのバインド
(.Net)DataGridViewでバインドしたデータソースを変更してもグリッドには反映されない?でカスタムクラスのバインドについて書きました。

あの方法でバインドした DataGridView を下記のようにプログラム側から1行削除しようとしました。

public partial class Form1 : Form
{
//カスタムクラスのList。バインド元になるデータ。
List<user> mlst = new List<user>();

private void button1_Click(object sender, EventArgs e)
{
mlst.Add(new user("hogehoge1", "1"));
mlst.Add(new user("hogehoge2", "2"));
mlst.Add(new user("hogehoge3", "3"));
}
 
private void Form1_Load(object sender, EventArgs e)
{
//バインド。
//dataGridView1.DataSource = mlst;
}

private void button2_Click(object sender, EventArgs e)
{
dataGridView1.Rows.RemoveAt(0);
}
}
 
//カスタムクラス。
public class user
{
public string name { get; set; }
public string id { get; set; }
public user(string sname, string sid)
{
name = sname;
id = sid;
}
}

Button1を押下しレコード追加後、Button2を押下してレコードを削除します。

その時、RemoveAt で下記の例外が発生しました。

System.InvalidOperationException はハンドルされませんでした。
Message="変更通知をサポートし、削除を許可する IBindingList に DataGridView がデータバインドされていない限り、プログラムで行を削除することはできません。"
Source="System.Windows.Forms"
StackTrace:
場所 System.Windows.Forms.DataGridViewRowCollection.RemoveAt(Int32 index)
場所 DataGridViewBindObjectTest.Form1.button2_Click(Object sender, EventArgs e) 場所 D:\mydoc\DataGridViewBindObjectTest\DataGridViewBindObjectTest\DataGridViewBindObjectTest\Form1.cs:行 65
...



で、調査してみました。
するとデータバインディングのおべんきょ。その5。で List をバインド対象として使わないほうがいいと書いてあります。理由として、List クラスが IBindingList インターフェイスを実装してないからみたいですね。
もうちょっと詳しい話が、MSDN:カスタム データ バインド (第 2 部)に下記のように書いてます。
BindingList<T> は IBindingList インターフェイス (System.ComponentModel 名前空間) のジェネリック実装です。また、リスト データ ソースで編集機能をサポートするために、データ バインドインフラストラクチャで最低限必要になるインターフェイスです。IList はバインド可能なリスト データソースの実装に必要な最低限のインターフェイスですが、このインターフェイスでは編集機能は提供されません。これは、ListBox などの編集できないコントロールにバインドされる場合は問題ありません。ただし、DataGridView など、編集機能を完全にサポートしているコントロールにバインドされる場合は、並べ替え、検索、インデックス作成、および変更通知などの機能のサポート以外に、リスト データ ソース内での編集機能が必須になります。IBindingList は IList から派生しているので、このような機能をすべてサポートするように拡張されます。

つまり、IListを継承した List<T>クラスは編集機能を有さない最低限の機能しかないためダメだったんですね。

かといって、今更BindingListに直すのも面倒なんでいい方法が無いかいろいろ試してみました。
見つけたのが、MSDN:方法 : オブジェクトを Windows フォーム DataGridView コントロールにバインドするです。
BindingSource を使ってますね。
DataGridView → BindingSource → コレクション という感じに使うようです。

ただ、MSDNのチュートリアルでは BindingSource の Add メソッドでカスタムクラス単体を追加しています。
本来はこの方法か、iBindingList を実装したクラスでバインドするのが筋なんでしょうが、面倒なので、下記のようにしました。

public partial class Form1 : Form
{
List<user> mlst = new List<user>();
 
private void button1_Click(object sender, EventArgs e)
{
mlst.Add(new user("hogehoge1", "1"));
mlst.Add(new user("hogehoge2", "2"));
mlst.Add(new user("hogehoge3", "3"));
bindingSource1.DataSource = null;
bindingSource1.DataSource = mlst;
}
 
private void Form1_Load(object sender, EventArgs e)
{
DataColumn clm1 = new DataColumn("name");
mTbl.Columns.Add(clm1);
DataColumn clm2 = new DataColumn("id");
mTbl.Columns.Add(clm2);
 
dataGridView1.DataSource = bindingSource1;
}
 
private void button2_Click(object sender, EventArgs e)
{
dataGridView1.Rows.RemoveAt(0);
}
}
 
public class user
{
public string name { get; set; }
public string id { get; set; }
 
public user(string sname, string sid)
{
name = sname;
id = sid;
}
}

BindingSource の DetaSource に List を指定しています。
とりあえずレコードの削除やDataGridView上の変更も List オブジェクトに反映されてるようです。

あんまきれいなやり方では無いですが、とりあえずこれでやってみることとしました。


今まで DataGridView にバインドしてたのは DataTable で楽してたんですが、他のコレクションをバインドするのは結構機能面の制約があって面倒ですね。。。
[PR]
by jehoshaphat | 2010-04-18 07:35 | .Net開発 | Trackback | Comments(0)
(.Net)DataGridViewでバインドしたListを変更してもグリッドには反映されない?
DataGridViewにカスタムクラスのListをバインドします。
そのカスタムクラスListにデータを追加したんですが、DataGridView に表示されません。

ちょうどソースとしてはこんな感じです。


public partial class Form1 : Form
{
  //カスタムクラスのList。バインド元になるデータ。
List<user> mlst = new List<user>();

private void button1_Click(object sender, EventArgs e)
{
mlst.Add(new user("hogehoge1", "1"));
mlst.Add(new user("hogehoge2", "2"));
mlst.Add(new user("hogehoge3", "3"));
}
 
private void Form1_Load(object sender, EventArgs e)
{
//バインド。
dataGridView1.DataSource = mlst;
}
}

//カスタムクラス。
public class user
{
public string name { get; set; }
public string id { get; set; }
public user(string sname, string sid)
{
name = sname;
id = sid;
}
}


Button1を押下したらデータソースとなっているListにデータを追加してるんで、DataGridViewにも反映してしてほしいんですが期待通りにはなりません。

下記のように button1_Click イベントの中で一旦 DataGridView.DataSource を null にして再バインドすると表示されました。

private void button1_Click(object sender, EventArgs e)
{
mlst.Add(new user("hogehoge1", "1"));
mlst.Add(new user("hogehoge2", "2"));
mlst.Add(new user("hogehoge3", "3"));
//一旦nullに
dataGridView1.DataSource = null;
//再バインド
dataGridView1.DataSource = mlst;
}


で、これでハッピー思ったんですが、 button1_Click イベント後表示されたセルをクリックすると、フォームの ShowDialog() でこんな例外が。。

System.IndexOutOfRangeException はハンドルされませんでした。
Message="インデックス -1 に値がありません。"
Source="System.Windows.Forms"
StackTrace:
場所 System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
場所 System.Windows.Forms.CurrencyManager.get_Current()
場所 System.Windows.Forms.DataGridView.DataGridViewDataConnection.OnRowEnter(DataGridViewCellEventArgs e)
場所 System.Windows.Forms.DataGridView.OnRowEnter(DataGridViewCell& dataGridViewCell, Int32 columnIndex, Int32 rowIndex, Boolean canCreateNewRow, Boolean validationFailureOccurred)
場所 System.Windows.Forms.DataGridView.SetCurrentCellAddressCore(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick)
場所 System.Windows.Forms.DataGridView.OnCellMouseDown(HitTestInfo hti, Boolean isShiftDown, Boolean isControlDown)
場所 System.Windows.Forms.DataGridView.OnCellMouseDown(DataGridViewCellMouseEventArgs e)
場所 System.Windows.Forms.DataGridView.OnMouseDown(MouseEventArgs e)
場所 System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
場所 System.Windows.Forms.Control.WndProc(Message& m)
場所 System.Windows.Forms.DataGridView.WndProc(Message& m)
場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
場所 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
場所 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
場所 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
場所 System.Windows.Forms.Application.Run(Form mainForm)
場所 DataGridViewBindObjectTest.Program.Main() 場所 D:\mydoc\DataGridViewBindObjectTest\DataGridViewBindObjectTest\DataGridViewBindObjectTest\Program.cs:行 18
.....


で、Form1_Load イベントでのデータソースの設定を外すとこの例外がおきなくなりました。

public partial class Form1 : Form
{
//カスタムクラスのList。バインド元になるデータ。
List<user> mlst = new List<user>();

private void button1_Click(object sender, EventArgs e)
{
mlst.Add(new user("hogehoge1", "1"));
mlst.Add(new user("hogehoge2", "2"));
mlst.Add(new user("hogehoge3", "3"));
}
 
private void Form1_Load(object sender, EventArgs e)
{
//バインド。
//dataGridView1.DataSource = mlst;
}
}
 
//カスタムクラス。
public class user
{
public string name { get; set; }
public string id { get; set; }
public user(string sname, string sid)
{
name = sname;
id = sid;
}
}



DataTable をバインドしていると、下記のようにフォームロード時に一回 DataTable にバインドするだけで、後は何もしなくても自動で反映されるんですが、やはりカスタムクラスのバインドはひと癖あるようです。
public partial class Form1 : Form
{
DataTable mTbl = new DataTable();
 
private void button1_Click(object sender, EventArgs e)
{
DataRow row1 = mTbl.NewRow();
row1["name"] = "hoge1";
row1["id"] = 1;
mTbl.Rows.Add(row1);
DataRow row2 = mTbl.NewRow();
row2["name"] = "hoge2";
row2["id"] = 2;
mTbl.Rows.Add(row2);
//この時点でDataGridViewが更新される。
}
 
private void Form1_Load(object sender, EventArgs e)
{
DataColumn clm1 = new DataColumn("name");
mTbl.Columns.Add(clm1);
DataColumn clm2 = new DataColumn("id");
mTbl.Columns.Add(clm2);
 
dataGridView1.DataSource = mTbl;
}
}



追記:
コメントに書いてもらったように、Listではなく、BindingList を使えば再バインドなしでできるようです。時間あれば試してみたいと思います。
[PR]
by jehoshaphat | 2010-04-18 07:24 | .Net開発 | Trackback | Comments(6)
(.Net)DataGridViewのCellFormattingイベント発生タイミングにやられた
(.Net)バインドされたDataGridViewでソートするとセルの色が元に戻るで、セルの値に基づいてDataGridViewのスタイルを変更するにはCellFormattingを使うべきだ と書きました。

ところが、CellFormattingイベントでは対応できない場面が出てきました。

問題となるのは、スタイル変更の判定元となるセルが横スクロールしないと表示されないようなグリッドの場合です。

その場合、そのセルがスクロールされて表示されないと CellFormatting が走らないため初期段階では背景色が変わりません。

列を先頭に持ってきてもいいがそうはできないので、下記のように対応しました。

private void RowStyleChange_Disable()
{
if (FilterTypeValue == Utility.FilterType.UserSearch) return;
foreach (DataGridViewRow item in dgvChild.Rows)
{
if ((bool)item.Cells["Disable"].Value == true){
item.DefaultCellStyle.BackColor = Color.Red;
}else{
item.DefaultCellStyle.BackColor = null;
}

}
}
 
//バインドしたときにスタイル変更
private void dgvChild_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
RowStyleChange_Disable();
}

結局CellFormattingイベントで処理するのをやめて、バインドしたときとソート後に手動(RowStyleChange_Disableメソッド呼び出して)で背景変更するようにしました。
[PR]
by jehoshaphat | 2010-04-17 00:50 | .Net開発 | Trackback | Comments(0)
(.Net)DataGridViewでカスタムクラスをバインドした状態でソートを行う。
(.Net)DataGridViewでカスタムクラスをバインドした状態でソートを行う。

DataGridViewで、カスタムクラス(独自クラス)のリストをバインドすると、ソートができません。
ソートグリフ(列ヘッダに表示される▽△マーク)も表示されません。

例えば下記のようになカスタムクラスのリストをDataGridViewにバインドするコードがあるとします。(C#)
(文字数制限のため今回はハイライトなしです。)

public partial class Form1 : Form{
private void Form1_Load(object sender, EventArgs e) {
//カスタムクラスのリスト生成
List<TestClass2> lstCls = new List<TestClass2>();
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<TestClass2>{
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<TestClass2> lst = (List<TestClass2>)drg.DataSource;
//ソート対象の列決定する
cmp.SortProperty = clm.DataPropertyName;
//ソートを実行する
lst.Sort(cmp);
//グリッド更新
drg.Refresh();

[PR]
by jehoshaphat | 2010-04-17 00:47 | .Net開発 | Trackback | Comments(0)