C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[7]: DataGridでの例外回避


(過去ログ 25 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■11551 / inTopicNo.1)  DataGridでの例外回避
  
□投稿者/ シャノン (231回)-(2007/12/17(Mon) 20:13:38)

分類:[.NET 全般] 

2007/12/17(Mon) 20:16:43 編集(投稿者)
お世話になります。
何かいい案があればご教示ください。

VS.NET 2003 + C# でクラサバアプリを作っています。
忌まわしき DataGrid での悩みです。

DB から取ってきたデータを、ごにょごにょ加工して、
DataGrid にバインドしてあるテーブルに追加しています(※)
(DB から取ってきた生データを持っているテーブルを直接バインドしていません)。

DataGrid は実際には DataGrid 派生クラスでして、
行が選択されたことをイベントとして検出するために、
DataGrid.ListManager.CurrentChanged をハンドリングして、
自前で実装した SelectedItemChanged イベントを発生させています。
SelectedItemChanged イベントの中では、選択された行を
取得するために、やはり実装した SelectedItem プロパティを取得しています。

SelectedItem プロパティのコードはこんな感じです。

public object SelectedItem
{
	get
	{
		if( this.ListManager == null )
		{
			return null;
		}

		if( this.ListManager.Count == 0 ||
			this.ListManager.Position == -1 ||
			! this.IsSelected( this.ListManager.Position ) )
		{
			return null;
		}

		return this.ListManager.Current;
	}
}

で、※部分では、DBから取得した行を一行ずつループして、
表示用に加工しつつ、バインドしてある表示用テーブルに追加しています。
ここでは、表示用テーブル.Rows.Add を使っているのですが、
これをやると、一行追加するごとに、DataGrid 派生クラスの
ListManager.CurrentChanged が発生し、イベントハンドラ中で
SelectedItem が取得されます。
その際、バインドされているテーブルにはデータが追加されているが、
DataGrid 側にはまだ反映されていないタイミングで処理が走ると思われ、
ListManager.Count が 0 でないにもかかわらず、
IsSelected( 0 ) で OutOfRangeException が発生してしまいます。

IsSelected を使わなければ例外は出ないのですが、その場合、
選択色になっていないがアクティブな行(グリッドの左端に三角がついている行)
も取得してしまいます。
これを、選択状態になければ取得しないようにしたいので、IsSelected
で判定しています。

突っ込みどころがたくさんあるような気がして、あえて焦点をぼかした
質問にしてみました。
例外が出ないようにするにはどうしたらいいでしょう? 以前の問題として
おかしいところがあったらご指摘ください。
ただし、あまりに根本的過ぎる問題は対処できない可能性が大きいですorz

引用返信 編集キー/
■11607 / inTopicNo.2)  Re[1]: DataGridでの例外回避
□投稿者/ れい (314回)-(2007/12/18(Tue) 22:11:44)
No11551 (シャノン さん) に返信
> 突っ込みどころがたくさんあるような気がして、あえて焦点をぼかした
> 質問にしてみました。
> 例外が出ないようにするにはどうしたらいいでしょう? 以前の問題として
> おかしいところがあったらご指摘ください。

一見よさげなのに、なんかうまくいかないということは、
おそらく根本的な方針が間違ってるのだと思うのですが、
使わなくなって久しいので、
もうぜんぜん覚えてないです。

で、無責任に言うと。

ぼんやり覚えてるところによると、
CurrencyManager.Positionはダメで、
DataGrid.CurrentRowIndexを使うのがよかったのではないかと思います。
試してませんが。

あと、できればミニマムコードがあるといいのではないかと。

#私はVS2008導入に伴い、VS2003は消してしまいました。
#2.0でも同じ問題が発生しそうな気がしますので、
#ミニマムコードがあれば試すくらいはできるかなぁ。

引用返信 編集キー/
■11736 / inTopicNo.3)  Re[2]: DataGridでの例外回避
□投稿者/ シャノン (232回)-(2007/12/20(Thu) 17:23:26)
No11607 (れい さん) に返信

お返事ありがとうございます。

> ぼんやり覚えてるところによると、
> CurrencyManager.Positionはダメで、
> DataGrid.CurrentRowIndexを使うのがよかったのではないかと思います。
> 試してませんが。

ダメでした。
DataGridに行が一行もないとき、CurrentRowIndexは0になります。
この状態でIsSelected(0)すると落ちてしまいます。

> あと、できればミニマムコードがあるといいのではないかと。

こちらにupしました。
http://www.technoledge.info/DGMin.zip

よろしくお願いいたします。
引用返信 編集キー/
■11753 / inTopicNo.4)  Re[3]: DataGridでの例外回避
□投稿者/ れい (337回)-(2007/12/20(Thu) 23:54:20)
No11736 (シャノン さん) に返信
> こちらにupしました。
> http://www.technoledge.info/DGMin.zip

ちょっとずつ思い出してきました。

・DataGridはCurrencyManagerのイベントを使って自分を更新している。
・現在の位置が変わったときCurrencyManagerはCurrentChangedで通知する。
・イベントハンドラは呼び出される順序が保証できない。

なので、CurrencyManagerのイベント処理をしてる最中に
DataGridの関連するプロパティをいじると、おかしくなります。
イベントハンドラの追加順とかで動かなくなったり。
私のところではIsSelectedもちゃんと動きませんでした。

CurrentChangedやPositionChangedイベント終了後に呼ばれるイベントがあればいいのですが、
CurrencyManagerにはありません。

DataGrid自身が出すイベント、CurrentCellChangedを使うのが順当かと思いましたが、
CurrentCellChangedイベント終了後に行がSelectされるため、IsSelectedが使えません。

ならDataGridにそういったイベントがあるのかと探すと、MouseDownとかKeyDownとかを使うしかない。
ためしに作ったら一応動いてはいますが、
本当に全部うまく動くのか確信が持てない所が問題です。

結局、DataGridはSelectedが変わったときにイベントを投げない仕様なので、
SelectedItemChangedを自分で実装するのは困難であるということになります。
CurrentCellChangedはきちんと動くので、セル単位で処理せよということでしょう。

イベントドリブンで設計がダメだとこういうことになるんですよね。
やっぱりDataGridは嫌いだ。

一応、なんとなく動いてるものを書いときます。

    class DataGridEx : DataGrid
    {
        private int _selectedindex = -1;
        public event EventHandler SelectedItemChanged;
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            CheckSelectedIndex();
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            CheckSelectedIndex();
        }
        protected override void OnKeyDown(KeyEventArgs ke)
        {
            base.OnKeyDown(ke);
            CheckSelectedIndex();
        }
        protected override bool ProcessDialogKey(Keys keyData)
        {
            bool r = base.ProcessDialogKey(keyData);
            CheckSelectedIndex();
            return r;
        }
        protected override bool ProcessKeyPreview(ref Message m)
        {
            bool r = base.ProcessKeyPreview(ref m);
            CheckSelectedIndex();
            return r;
        }
        public object SelectedItem
        {
            get
            {
                if (_selectedindex < 0) return null;
                return this.ListManager.List[_selectedindex];
            }
        }

        private void CheckSelectedIndex()
        {
            int newindex;
            if (this.ListManager == null) newindex = -1;
            else if (this.ListManager.Count == 0) newindex = -1;
            else if (this.ListManager.Position == -1) newindex = -1;
            else if (!this.IsSelected(this.ListManager.Position)) newindex = -1;
            else newindex = this.ListManager.Position;
            if (_selectedindex == newindex) return;
            _selectedindex = newindex;
            if (SelectedItemChanged != null) SelectedItemChanged(this, EventArgs.Empty);
        }
    }

引用返信 編集キー/
■11791 / inTopicNo.5)  Re[4]: DataGridでの例外回避
□投稿者/ シャノン (233回)-(2007/12/21(Fri) 12:15:09)
No11753 (れい さん) に返信

ありがとうございます。

> 私のところではIsSelectedもちゃんと動きませんでした。

IsSelected「も」と言うより、まさに IsSelected が問題なのです。

> ならDataGridにそういったイベントがあるのかと探すと、MouseDownとかKeyDownとかを使うしかない。
> ためしに作ったら一応動いてはいますが、
> 本当に全部うまく動くのか確信が持てない所が問題です。

ですねぇ。

> イベントドリブンで設計がダメだとこういうことになるんですよね。
> やっぱりDataGridは嫌いだ。

というよりも、セル単位用に設計されているものを行単位で利用しようとしているのがダメなんでしょうね。
行単位選択にして、読み取り専用にして、CtrlやShiftで複数選択可能にして、行の背景色を変えられるようにして、Tabキーでセル間遷移しないようにして…実際には山ほどカスタマイズして使ってます。もう手放したいorz
ただ、行単位Gridって需要あると思うのに、標準で提供してくれていなかったという点では、やっぱり嫌いです。
2.0のDataGridViewは使いやすいんでしょうか…気になります。

> 一応、なんとなく動いてるものを書いときます。

ありがとうございます。参考にさせていただきます。

# ミニマムソースは削除しました。
解決済み
引用返信 編集キー/
■11793 / inTopicNo.6)  Re[5]: DataGridでの例外回避
□投稿者/ れい (340回)-(2007/12/21(Fri) 12:26:04)
No11791 (シャノン さん) に返信
>>イベントドリブンで設計がダメだとこういうことになるんですよね。
>>やっぱりDataGridは嫌いだ。
>
> というよりも、セル単位用に設計されているものを行単位で利用しようとしているのがダメなんでしょうね。

んー。これが微妙で。セルなのか行なのか、不明です。

CurrencyManagerは行単位で動きます。
DataGridだって、行ヘッダーがあり、削除は行ごとです。
DataGrid内部は行ごとにできてるっぽいのに、
イベントはセル単位しかなく、プロパティもセル単位。
なのにIsSelectedは行単位。

統一の取れてないだめな設計だと思います。
AccessとかVB6とかの影響を悪く受けてる気がします。

行単位で選択できるものなら、
変更があったときにイベントを投げるように作っておくべきなのに。

> 2.0のDataGridViewは使いやすいんでしょうか…気になります。

はるかにまし、というか、良いです。戻れません。
でもまだ気になるところはあるかなぁ。
引用返信 編集キー/
■11794 / inTopicNo.7)  Re[6]: DataGridでの例外回避
□投稿者/ シャノン (235回)-(2007/12/21(Fri) 12:33:16)
2007/12/21(Fri) 12:43:30 編集(投稿者)

No11793 (れい さん) に返信

>>というよりも、セル単位用に設計されているものを行単位で利用しようとしているのがダメなんでしょうね。
>
> んー。これが微妙で。セルなのか行なのか、不明です。

もう一次元ありますよ。
DataGridは1つのコントロールで複数のテーブルを扱えます。
その時にIsSelectedがどう振舞うのかは試していません。
というか、複数テーブルなんて使ってる人がいるのか疑問です。

>>2.0のDataGridViewは使いやすいんでしょうか…気になります。
>
> はるかにまし、というか、良いです。戻れません。
> でもまだ気になるところはあるかなぁ。

いいなぁ…
解決済み
引用返信 編集キー/
■11796 / inTopicNo.8)  Re[7]: DataGridでの例外回避
□投稿者/ れい (342回)-(2007/12/21(Fri) 12:39:40)
No11794 (シャノン さん) に返信
> もう一次元ありますよ。

ぐはっ

忘れてた。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -