2013年5月24日金曜日

DataGridViewの先頭行が勝手に消える

初のまともな投稿は.NETネタ。

何かと問題が多いWindows FormsのDataGridViewですが、こいつのせいでまた迷惑こうむりました。
現象としては「先頭行が勝手に消える」というもの。


サンプルコードをば。
まずは、新規プロジェクトを追加し、フォームに適当なBindingSourceとDataGridViewを追加します。
また、コード側にはNameプロパティとAgeプロパティを持ったPersonクラスを作っておきます。
で、BindingSourceのDataSourceをPersonクラスにして、DataGridViewのDataSourceはBindingSourceにする。
この状態で実行すると、フォームにはName列とAge列がある行追加可能なDataGridViewが表示されるはず。


ここでButtonを1個追加し、クリック処理に下記のような処理を記述します。

        private void button1_Click(object sender, EventArgs e)
        {
            bindingSource1.DataSource = new List<Person>();

            dataGridView1.Focus();
            dataGridView1.AllowUserToAddRows = false;

            bindingSource1.DataSource = new List<Person>()
            {
                new Person(){Name = "一郎", Age = 10},
                new Person(){Name = "次郎", Age = 9},
            };
        }

これで、ボタンを押すと画面のグリッドに一郎さんと次郎さんが表示され、行追加が不可能になります。

…が。なぜか次郎さんを選択した瞬間に一郎さんが消えてしまいます。

これ、キモになる条件は
①BindingSourceを使ってデータ連結されている新規行追加可能なグリッドで、
②データソースに空のリストが設定されている状態で、
③データグリッドにフォーカスを当て、そのままフォーカスを外さずに、
④データグリッドのAllowUserToAddRowsプロパティをfalseに変更し、
⑤その後、データソースに空ではないリストを設定した場合
という感じです。

ざっくりとした理屈ですが、新規行追加状態のグリッドにフォーカス当たると、
グリッドの内部的なステータスが「新規行追加中」になります。
その状態で新規行追加を封じることによって、内部ステータスと実際の状態の間に不整合が発生し、
あとで一郎さんのRowValidatingが発生した際に、新規行の登録をキャンセルしたものとみなされて行が消える、
というのがおおまかな流れのようです。

ちなみにデータソースにList<T>ではなくBindingList<T>を使用していた場合はこの現象発生しません。
まあ、BindingSource使うならBindingList<T>使えってことなんでしょうかね。


DataGridViewはちょっと条件が揃うと急に謎な動きをして未だに怖いですね。
WPFのDataGridよりはだいぶマシだと思いますがw

0 件のコメント:

コメントを投稿