QA@IT
この質問・回答は、@ITの旧掲示板からインポートされたものです。

DataGridViewの新規行について

VB2005でWindowsアプリを開発しています。

DataGridViewをバインドして使用していて下記の現象ではまってしまいました。

新規の行でセルに何か入力すると最終行に空白の行が追加されますが、その後
Enterでセルの編集を終えた場合はバインドしたテーブルには追加された新規行
は含まれていません。

しかし、Enterを押さずに今追加された最終行のセルをマウスでクリックして
編集を終えた場合、新規行が含まれてしまいます。
(最終レコードに空白の行が登録されている)

マウスでクリックしたときにバインドしたテーブルに空白の行が登録されるのを
防ぐ方法がありましたら教えてください。

よろしくお願いします。

2007/08/21 18:26 追記
レスが付かないようなので質問を変えさせていただきます。

皆さんは上記の様な現象が起こったことはないでしょうか?
私の作ったアプリ固有の現象なのかDataGridViewの仕様なのかが分かるだけでも
助かります。
[ メッセージ編集済み 編集者: Wish 編集日時 2007-08-21 18:33 ]

質問者:Wish

回答

パッとみて気がついたことですが少しでもキッカケになれば。
(既に認識されていたらごめんなさい)

「新規の行」、「空白の行」、「今追加された最終行」の関係を読み取ることが
できていませんが、「クリックによる追加」自体は既定の動作ではないですか?
http://msdn2.microsoft.com/ja-jp/library/ak81b67y.aspx

「空白の行」を表示することは「AllowUserToAddRows」プロパティを「False」に
することでやめられるようですが、必要な機能がこれでは満たせないということに
なるのでしょうか。

投稿者:Bessi

編集 履歴 (0)

Bessi様返信ありがとうございます。

レスが付かなかったのは、私の書き方が悪くてうまく皆さんに伝わっていなかった
みたいですね。すみませんでした。

データ登録に使用するため、行が追加されるのは構わないのです。

バインドしたテーブルのレコードが0件の場合、DataGridViewには何も入力されていない
行が1行表示されていますがこれが「新規の行」です。

この状態で「新規の行」に何か入力すると自動的にDataGridViewの最終行に何も入力されていない
行が追加されますがこれが「空白の行」です。

「今追加された最終行」は上記で自動的に追加された「空白の行」です。

例えば上記の例で行くと、「空白の行」が出来た直後(まだセルの編集中)にEnter
でセルを移動するとDataGridViewには2行表示されていますがデータソースのレコード
は1件です。その後「空白の行」に何か入力しない限りは、「空白の行」をマウスで
クリックしようがデータソースのレコードは1件です。

しかし「空白の行」が出来た直後Enterを押さずにその「空白の行」をクリックすると
データソースのレコードが2件になっているのです。
この2件になる状態を防ぎたいのです。

旨く説明できなくて申し訳ありませんが宜しくお願いします。

投稿者:Wish

編集 履歴 (0)

以下のテストコード(VB.NET2005)を作って試してみましたが、現象がでません(コードが悪いのかもしれませんが)。
最小コードでテストしてみてはどうでしょうか?



Public Class Form1

    Private Sub ShowCount()
        Dim dt As DataTable = DirectCast(Me.DataGridView1.DataSource, DataTable)
        Me.Label1.Text = dt.Rows.Count.ToString
    End Sub

    Private Sub DataGridView1_CellClick(ByVal sender As Object, _
                ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellClick
        ShowCount()
    End Sub

    Private Sub DataGridView1_Click(ByVal sender As Object, _
                ByVal e As System.EventArgs) Handles DataGridView1.Click
        ShowCount()
    End Sub

    Private Sub DataGridView1_KeyUp(ByVal sender As Object, _
                ByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView1.KeyUp
        ShowCount()
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, _
                ByVal e As System.EventArgs) Handles Me.Load
        Dim dt As New DataTable
        dt.Columns.Add("CD")
        dt.Columns.Add("NAME")
        Me.DataGridView1.DataSource = dt
    End Sub

End Class

投稿者:まるく

編集 履歴 (0)

DataGridView を何にバインドして、
件数はどのオブジェクトのCountで確認していますか?

今、検証できる環境にないのですが、
BindingSource 経由でバインドされている場合は、
追加行は BindingSource に追加されて、保留状態になるので注意が必要です。

BindingSource 経由で ADOの DataView にバインドしている場合で説明します。

(1) DataGridView の追加行(一番下の空行)にカーソルを移動する。

   BindingSource に行が追加される。
   ただし、この時点では追加された行は BindingSource の中で
   保留状態になっており、DataView には反映されていない。
   この時点で BindingSource の件数=DataView の件数+1

(2) (1)の後、セルに何か入力し、カーソルを別の行に移動する。

   BindingSource で保留されていた追加行が確定され、
   DataView に反映される。
   カーソルが移動した先の行が追加行でない場合は、
   この時点で BindingSource の件数=DataView の件数
   カーソルが移動した先の行が追加行の場合は、
   (1)の理屈で、BindingSource には新しい行が追加されているので、
   この時点で BindingSource の件数=DataView の件数+1

(3) (1)の後、その行に何も入力せずに、カーソルを別の行に移動する。

   BindingSource で保留されていた追加行がキャンセルされ、
   BindingSource から削除される。
   この時点で BindingSource の件数=DataView の件数

こういう話があるので、
Wishさんが「件数」とおっしゃっているのが、
BindingSource の Count なのか、
DataView などのデータソースの Count なのかによって、
意味が変わってきます。
どちらで確認されていますか?

まるくさんの検証では、DataTable を直接バインドしていますから、
BindingSource 経由でバインドしてみると再現するかも知れません。

投稿者:KI

編集 履歴 (0)

まるく様、KI様 ありがとうございます。

返事が遅くなってすみません。

KI様、DataGridViewににはDataTableを直接バインドしています。

まるく様のコードを実際のプログラムに近い形にしたところ同じ現象が確認できました。



    Private Sub DataGridView1_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataGridView1.LostFocus
        ShowCount()
    End Sub

    Private Sub DataGridView1_CellEndEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit

        '新規追加行なら処理を抜ける
        If DataGridView1.Rows(e.RowIndex).IsNewRow = True Then Exit Sub

        '名称設定
        DataGridView1.Rows(e.RowIndex).Cells("NAME").Value = "hogehoge"

    End Sub

まるく様のコードに上記コードを追加してください。

CDに何か入力して別のセルにフォーカスを移すとNAMEに自動的に値をセットします。
(実際の処理ではフラグの様なものをセットしています。)

その後DataGridView自体から別のコントロールにフォーカスを移すと件数が変化します。
(実際の処理では登録ボタン等をクリックするためフォーカスが外れる)

CellEndEditで値をセットするところが問題のようです。この箇所をはずすと
現象は出なくなりましたが今の作りでは必要な処理のためはずせません。

代替策等あるでしょうか?

投稿者:Wish

編集 履歴 (0)

毎度検証ができない状態での返答なので、はずしてたらすみませんが、
CellEndEdit の代わりに CellValueChanged を使うというのはどうですか?
これなら追加行かどうか判定する必要もないはずです。

投稿者:KI

編集 履歴 (0)

まるくさん、Wishさんのコードを単純にくっつけてみましたところ、
以下の事象が発生しました。

1.初期表示された行の【CD】列に値を入力する
=>アスタリスク表示の行が増える(現在2行)
2.増えた2行目をクリックする(現在2行)
3.【CD】列ヘッダーをクリックする
=>空白行が増える(現在3行)

そこでちょっと違う操作をしてみました。
1.初期表示された行の【CD】列に値を入力する
=>アスタリスク表示の行が増える(現在2行)
2.増えた2行目をクリックする(現在2行)
3.【ESC】キーを押す
=>何も変わらない(現在2行)
4.【CD】列ヘッダーをクリックする
=>何も変わらない(現在2行)

ということでとりあえずBessiさんが書かれていたように規定の動作の範囲で
動いているようです。

そしてなぜ空白行がそのままコミットされるのか検証してみる前にKIさんの
コメントをみつけましたのでやってみました。
見事に空白行が表示されることはなくなりました。

私自身はまだ初心者なので
【CellEndEdit】と【CellValueChanged】で結果がなぜ違うのかわかりません。
これから勉強します。

以上、ご参考になれば。

投稿者:プレマニア

編集 履歴 (0)

KI様、プレマニア様 ありがとうございます。

KI様の「CellEndEdit の代わりに CellValueChanged を使う」方法を実際のアプリに
組み込んでみたところHeaderTextをセットしたりデフォルト値をセットしたりするところでも
CellValueChangedイベントが発生するので、処理の見直しが必要ですが、問題となっていた
現象が回避出来るようになりました。

ありがとうございました。

投稿者:Wish

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。