【DataGridView】新規行で編集を確定するには?

解決


Kenichi  2007-05-18 02:55:38  No: 136420

DataGridViewにてあるデータのリストを
ポップアップ画面で表示するボタンを作成し、
そのボタンを用いて隣のセル(テキスト)に選択した値を
表示するといったプログラムを作成したのですが
下記のような問題が起こっております。

新規行でボタンをクリックし、名称が設定されたことを確認してから
マウスかカーソルでグリッドの行を変更すると設定した値が
消えてしまいます。
また、新規行に戻ると設定した値が復活します。
この状態で、いろいろな動作をしていると
「指定された引数は、有効な値の範囲内にありません。」
といったエラーが表示されます。

上記の動きは新規行のみ起こる現象なので、
ポップアップで選択されたらその行の
編集を確定させたらどうだろうと思い、質問させていただきました。

長くなってしまい申し訳ありませんが、
新規行の編集を確定する方法を
お分かりの方がいらっしゃいましたらお教え願えませんでしょうか。


魔界の仮面弁士  2007-05-18 03:54:53  No: 136421

> といったプログラムを作成したのですが

恐らくは DataTable を設定してのデータバインドによる実装に
なっているのだと思いますが、現時点においては、
利用者の視点から見た状況説明だけしかなく、具体的な
開発者の視点から見た内部説明がなされてないので、
これだけでは判断する事ができません。

まずは、DataGridView のどのイベントに何を書いているのかを
明らかにしてみてください。可能であれば、実際に現象を再現可能な
最低限のコードを提供してもらえると、話が進めやすいです。

> マウスかカーソルでグリッドの行を変更すると設定した値が
> 消えてしまいます。
まず、その設定したデータは確定されているのでしょうか。
それぞれのイベントにおいて、

  DataGridViewRow オブジェクトを取得
    → .DataBoundItem プロパティから、DataRowView を取得
    → .Row プロパティから、DataRow を取得
    → .RowState プロパティから、行の状態(RowState)を取得

として、RowState が Detached なのか Added なのかを調べてみましょう。

また、入力値に問題が無いかも検証してください。
たとえば、DataTable に NULL 入力不可な列があるのに、
新規追加時に、その列に値を設定していないかったなどの
状態にはなっていませんか?


Kenichi  2007-05-18 05:49:38  No: 136422

説明が足りず申し訳ありませんでした。
仰っている通りDataGridViewはDataTebleをセットしております。
テスト的なコードを記述しておきます。

Form1にはDataGridViewを貼り付けていただいて、
列をボタンとテキストを追加してください。
Form2にはリストボックスを貼り付けて、
Itemを2つぐらい追加してください。

下記コードを貼り付けると実装できるはずですので、
グリッドの1列目のボタンを押すと2列目のテキストに
ポップアップのデータが入るのですが、新規行の場合
リストから選んでグリッドに戻った際に
カーソルで上の行に行くとリストで選んだ値が消えてしまいます。

分かりにくいかも知れませんが、
よろしくお願いいたします。

Form1(データグリッドを持つフォーム)
-----------------------------------
Public Class Form1

    Private Sub DataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView.CellContentClick
        Dim frm As New Form2(DataGridView.Rows(e.RowIndex).Cells(1))
        frm.Show(Me)
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim table As New DataTable()
        Dim dataRow As DataRow = Nothing

        table.Columns.Add("Code")
        table.NewRow()
        dataRow = table.NewRow()
        dataRow("Code") = "01"
        table.Rows.Add(dataRow)

        Me.DataGridView.DataSource = table

    End Sub
End Class

From2(ポップアップ画面)
-----------------------------------
Public Class Form2
    Private m_name As DataGridViewCell
    Public Sub New(ByVal argname As DataGridViewCell)

        ' この呼び出しは、Windows フォーム デザイナで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。
        m_name = argname
    End Sub

    Private Sub ListBox1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseClick
        m_name.Value = Me.ListBox1.SelectedItem
        Close()
    End Sub
End Class


魔界の仮面弁士  2007-05-21 11:40:47  No: 136423

まとまった時間が取れたのでテストしてみましたが、なかなか難しい問題ですね。

> カーソルで上の行に行くとリストで選んだ値が消えてしまいます。
行ヘッダのアイコンを見る限り、そもそも編集が始まっていない感じですね。
まずは、新規行(Detached または Added な DataRow)を作成する必要がありそうです。

で、ヘルプを漁っていると、こんな記述を発見。

[IBindingList インターフェイス]
http://msdn2.microsoft.com/ja-jp/library/system.componentmodel.ibindinglist%28VS.80%29.aspx

> AddNew メソッドを呼び出す場合は、適切なインデックスの付いた
> ItemAdded 列挙体を使用して ListChanged イベントを発生させて
> ください。追加した新しい行は、DataGridView コントロールで
> Esc キーを押すと削除できる状態にあります。

新規行が作成され、しかも Esc キーでキャンセルできる状況ならば、まさに求めていた物だと
思われますが……外部から ListChanged イベントを発生させる方法がわかりませんでした。

一応、無理矢理な方法として、リフレクションを使って
[BindingList.OnListChanged メソッド]
http://msdn2.microsoft.com/ja-jp/library/ms132698%28VS.80%29.aspx
を呼び出すという手がありますが……正攻法とは呼べない気もしますね。

If m_name.OwningRow.IsNewRow Then
  Dim dgv As DataGridView = m_name.DataGridView
  Dim m As System.Reflection.MethodInfo = _
    GetType(CurrencyManager).GetMethod("OnListChanged", _
    System.Reflection.BindingFlags.Instance Or _
    System.Reflection.BindingFlags.NonPublic).Invoke( _
    dgv.BindingContext()(dgv.DataSource), New Object() { _
    New System.ComponentModel.ListChangedEventArgs( _
    System.ComponentModel.ListChangedType.ItemAdded, _
    dgv.NewRowIndex)})
End If
m_name.Value = ListBox1.SelectedItem
Close()


魔界の仮面弁士  2007-05-21 11:45:10  No: 136424

訂正。余計な行が紛れ込んでいました。m(_ _)m

If m_name.OwningRow.IsNewRow Then
  Dim dgv As DataGridView = m_name.DataGridView
  GetType(CurrencyManager).GetMethod("OnListChanged", _
   System.Reflection.BindingFlags.Instance Or _
   System.Reflection.BindingFlags.NonPublic).Invoke( _
   dgv.BindingContext()(dgv.DataSource), New Object() { _
   New System.ComponentModel.ListChangedEventArgs( _
   System.ComponentModel.ListChangedType.ItemAdded, _
   dgv.NewRowIndex)})
End If
m_name.Value = ListBox1.SelectedItem
Close()


Kenichi  2007-05-23 02:58:45  No: 136425

魔界の仮面弁士さん
ご返信ありがとうございます。

お調べ頂いたとおり編集が始まっていないのか
編集が終わっていない状態なのかわからない状態なので
件名で「新規行で編集を確定するには?」とお聞きさせて頂きました。

私のほうでも調べ続けていたところ
少し強引な方法で解決できる事が出来る情報が、
載っていましたので、とりあえず試してみました。
一応解決できたみたいなので、お知らせいたします。

詳細
Form1にDataSetとBindingSourceを貼り付けて、DataSetに
コード内で記述したDataTableを挿入します。
予めグリッドとDataSet、BindingSourceは、連結しておきます。

それからグリッドのCellValidatingイベントで、
新規行ならBindingSource.Currentに直接書込んでしまう事で
値が消えなくなりました。

ソースを載せたほうがよかったのですが、上記テストコードを
消してしまったので、文章でご説明させていただきました。

魔界の仮面弁士さん、その他お考え頂いた皆さん
本当にありがとうございました。


魔界の仮面弁士  2007-05-23 04:10:11  No: 136426

> 新規行ならBindingSource.Currentに直接書込んでしまう事で
あれ? ということはもしかして、バインド列に対する処理だったのでしょうか。
すみません、質問内容を読み違えていました。m(_ _;)m

>> 列をボタンとテキストを追加してください。
を行った後、提示されたコードをそのまま試したら
  列0 … [Button]非連結
  列1 … [TextBox]非連結
  列2 … [TextBox]Code列に連結
になったので、
>> Dim frm As New Form2(DataGridView.Rows(e.RowIndex).Cells(1))
は、(Code列ではなく)非連結列に対する処理だと思っていました…。(^^;


Kenichi  2007-05-23 22:29:46  No: 136427

魔界の仮面弁士さん

申し訳ありません。
提示したコードからでは、非連結の列になりますね。

提示したコードは、急ぎで作成したもので
実際のプログラムは、ボタン以外は連結列の
グリッドを使用しています。

質問内容が不明確で
誠に申し訳ありませんでした。
これからは注意していきたいと思いますので
今後、質問させて頂いた時もお力を貸してもらえると幸いです。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加