none
DataGridViewクリック一回目の入力は必ず失敗します!  解決策は? RRS feed

  • 質問

  • フォームがあり、そこに明細部としてDataGridViewを設けてあります。(フォーム名:F_I_Scrap)
    Form_Load( F_I_ScrapのLoad )内に、DataGridViewのDataSourceは明示してあります。

    別のフォーム(メニューフォーム)があり、そこのコマンドボタンを押すと → F_I_Scrap表示となっています。
    表示したフォームで、明細部に入力しようとすると(新規登録・修正時)、必ず一回目は入力できません。

    入力しても、元の(以前の)表示になってしまいます。 しかし、二回目からは入力が正常表示になります。

    つまり、クリック一回目の入力は必ず失敗します、二回目以降から成功します。
    初回から入力を正常にしたいので、いといろとやってみました。

    (1) DataGridView1.CellClick
      DataGridView1.EditMode = DataGridViewEditMode.EditOnEnter
      'DataGridView1.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2

            '1回のクリックでエディットモードにする処置
            DataGridView1.BeginEdit(True)

            '1回のクリックでエディットモードにする処置  --- SendKeys.Send("{F2}")  < SendKeys.SendWait("{F2}")
            SendKeys.SendWait("{F2}")

            'コンボ・クリックが二回なのを、一回で済ませる!--- SendKeys.SendWait("{F4}") > SendKeys.Send("{F2}")
            If Me.DataGridView1.Columns(e.ColumnIndex).Name = "CB_ItemName" _
                      AndAlso TypeOf Me.DataGridView1.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then
                SendKeys.SendWait("{F4}")
            End If

    (2)Form_Load内
     'DataGridView1の値の表示---F_I_Scrapの中のDataGridView1の内容表示---- 'DataGridView1のデータテーブル(DataSource)=dt_ScrapDetails
      DataGridView1.DataSource = dt_ScrapDetails
      'クリック一回で表示させる?!
       DataGridView1.BeginEdit(True)

    上記(1)・(2)ともに、うまくいきませんでした。
    また、Me.DataGridView1.Focus()を、F_I_Scrapの、いろんなところに入れてみましたが、どれも不成功でした。


    Form_Load( F_I_ScrapのLoad )内に、すでにdataGridViewのDataSourceは明示してあるのですが、
    メニューフォームのコマンドボタンのコード内にも下記コードを入れると、初回クリック時の入力不成功は解消され、一回目から正常に入力できるようになりました。


    メニューフォームのコマンドボタンのコード内にも下記コードを入れると成功:
    '**→
    'クリック一回で表示させたい
     '→ F(F_I_Scrap)の明細部(DataGridView)で、クリック一回目の入力は必ず失敗、二回目以降は成功する。初回から入力を正常にしたい!
     'F.DataGridView1.BeginEdit(True)---これは、エラー  → F.DataGridView1が存在しないから?
     'Fの明細部の変更:クリック一回で表示させるには、DataSourceを明示しなければらない?(これをしないと、一回目は必ず失敗、二回目以降は成功するが。)
     
            Dim sb2 As New System.Text.StringBuilder
            Dim ds_ScrapDetails = New DataSet
            Dim dt_ScrapDetails As New DataTable  'DataGridView1のデータテーブル(DataSource)
            adapter.SelectCommand = SQL
            sb2.Append("SELECT T_ScrapDetails.Scrap_ID") 

            ---------略-----

            sb2.Append(" FROM T_ScrapDetails")
            sb2.Append(" WHERE T_ScrapDetails.Scrap_ID= @Scrap_ID3 order by [LineNo]")
            SQL.Parameters.AddWithValue("@Scrap_ID3", ID_Max)
            SQL.CommandText = sb2.ToString()
            adapter.SelectCommand = SQL
            adapter.Fill(ds_ScrapDetails)
            dt_ScrapDetails = ds_ScrapDetails.Tables(0)
           
            F.Show()  ’F---フォーム名(F_I_Scrap)
            F.DataGridView1.DataSource = dt_ScrapDetails   ’ここが肝心! これで、初回クリック時から成功。

      '**←DataGridViewへのセル入力をClick一回で成功せるために、これを置く。


    これで問題は解決したのですが、これで良いのでしょうか?
    もっと、簡単なコード・設定法がある気がするのですが?
    クリック一回目の入力失敗については、二か月ほど悩んでいました。

    良い解決法がありましたら、教えてください。

    YKSaila
    補足:5/12~5/26くらいまで不在ですので、ご回答に、返事ができないかもしれません。
    帰宅次第、お礼・ご返事を書きます。

    2015年5月11日 5:56

回答


  • Handles DataGridView1.CellValidatingに問題があるようです。
     '****(1)以下の二行のコードを、DataGridView1.CellValidatedに入れ替えると、問題は解決します( DataGridView1.CellValidatingからは、'****(1)以下の二行のコードは削除)

     '****(1)のコード:
             Me.DataGridView1.CurrentRow.Cells("Amount").Value = CInt(Me.DataGridView1.CurrentRow.Cells("Volume").Value) * _
             CInt(Me.DataGridView1.CurrentRow.Cells("UnitPrice").Value)


    私の推測:
    当然、CellValidatingは、CellValidatedより前に起こる事象です。

    で、たぶん、CellValidatingでのCurrentRowは、第一行なのでしょう。

    で、一行目の数量欄(Volume)に数値を入れても、'****(1)が優先されてこの数値入力が反映されないと考えました。
    CInt(Me.DataGridView1.CurrentRow.Cells("Volume").Value)が優先されるのではないでしょうか?

    一行目の数量欄(Volume)に、初回数値を入れても(CellValidated)、まだCellValidatingの影響が残っているのでは、と考えています。
    それだけ、CellValidatingは強力なのかも?
    間違いかもしれませんが。


    '****(1)のコードを、.RowValidatingに入れ替えてもうまくいきます。

    ちなみに、'****(1)のコードを、.CellValidating / .CellValidated / .RowValidatingの全部に入れると、先の問題事象が発生します。
    .CellValidatingが影響しています。

    .CellValidatingから、'****(1)のコードを削除すれば、問題は解決します(.CellValidated / .RowValidatingの両方に残してもOKです。)

    YKsaila
    • 回答としてマーク yksaila 2015年6月4日 14:49
    • 編集済み yksaila 2015年6月4日 14:57
    2015年6月4日 14:48

すべての返信

  • こんにちは。

    便宜上、MenuFormとDetailForm(F_I_Scrap)と呼ばせて頂きますね。

    1. まず、MenuFormとDetailFormの関係はMenuFormからクリックで起動するだけでしょうか。パラメータなどは渡しますか?
    2. DetailFormのDataGridView1は.NET標準のDataGridViewですか?
    3. そもそものご質問である「入力できない」「失敗する」が少し曖昧です。(何が成功で何が失敗なのか…)
      DataGridViewのセル編集を行いたいがクリックで編集モードにならないという意味でしょうか?DetailFormを起動した時点で編集モードの状態にしたい、という意味でしょうか?

    • 回答の候補に設定 星 睦美 2015年5月19日 0:37
    • 回答の候補の設定解除 星 睦美 2015年6月4日 5:52
    2015年5月11日 6:21
    モデレータ
  • yksaila さま よろしく。

    親フォームから子フォームの DataGridView を初期設定して、子でデータ操作した場合に、
    思う様な挙動をしないと言う事でしょうか。

    各フォーム上の変数のスコープやインスタンスの取り方にもよると思うので、一概には言えませんが、
    所属するクラスやスコープをあたる事で、答えが見えてきそうですが。
    (個々のコードでの変数のアクセスが、思ったものにアクセスしていない 事を想像しています。)

    色々な方法や書き方があると思うので、以下は、参考で読んで下さい。
    一番、容易なのは、親に Friend でデーターソースを用意して、全て、それにアクセスする方法でしょうか。
    (子からは、親のクラスを明示してアクセスするとか。)

    抽象的、ニュアンス的な、指摘で申し訳ありません
    • 回答の候補に設定 星 睦美 2015年5月19日 0:37
    • 回答の候補の設定解除 星 睦美 2015年6月4日 5:52
    2015年5月11日 7:35
  • Tak1Wa様

    質問1について:MenuFormにコマンドボタンを設けて、その中に以下のコードを書いています。

    Dim a As Integer
    a = DataGridView1.CurrentCell.RowIndex    '行インデックス(行番号:+1)
     If NZint(Me.DataGridView1.Rows(a).Cells("TradeType").Value) = 101 Then   'F_I_Scrap
        Dim F As New F_I_Scrap
        F.TX_Scrap_ID.Text = Convert.ToString(DataGridView1.Rows(a).Cells("Scrap_ID").Value)
        F.TX_ScrapNo.Text = Convert.ToString(DataGridView1.Rows(a).Cells("ScrapNo").Value)
               -----略-----
        'コンボ表示は F.Show()の後!

        Dim g As Graphics = F.CreateGraphics
        F.Show()
        'コンボ表示は F.Show()の後!
        F.CB_Client_ID.SelectedValue = NZint(DataGridView1.Rows(a).Cells("Client_ID").Value)
        F.CB_Staff_ID.SelectedValue = NZint(DataGridView1.Rows(a).Cells("Staff_ID").Value)
        F.CB_Operate_Staff_ID.SelectedValue = NZint(DataGridView1.Rows(a).Cells("Operate_Staff_ID").Value)
     End If

    質問2について:NET標準のDataGridView?

    意味が正確には分かりませんが、そうだと思います。datatableを作成しそれをdatasourceにしています。

    質問3について:

    DetailFormは、MenuFormのコマンドボタンからしか起動しません。一回目のクリックでは編集モードにならないということです。

    MenuFormのコマンドボタン→DetailFormが開く→明細部(DataGridView)に入力

    例:明細部の数量セル(開いた時点では0表示)に、9を入力。enterまたは他のセルに移動しても、9は入力されません。0のままです。

    これが、初回入力の症状です。二回目以降は正常に9と表示されます。これ以降は、他のセルへの入力は正常表示されます。

    最初に表示したときは、どのセルでも初回の入力は失敗します(上記の意味で)、で二回目以降は入力したとおりに表示します。

    (MenuFormのコマンドボタンのコードの中に、DetailFormのdatasourceを指定しないときにそうなります)

    以上です、よろしく、お願いします。

    YKLsaila

    2015年5月30日 23:51
  • ShiroYuki_Mot様

    >親フォームから子フォームの DataGridView を初期設定して、子でデータ操作した場合に、
    >思う様な挙動をしないと言う事でしょうか。

    いや、そのようにしたときは正常動作します。

    子フォームのForm_Loadのコード内にはDatagridViewのdatasourceを指定していますが、親フォームから子フォームを開くときにはDatagridViewのdatasourceを指定しない場合です。

    親フォームのコマンドボタン内のコードには、子フォームを開く(F.Show)という指示だけで、DataGridViewのdatasourceを指定しないのです。 子フォームのForm_Load内に、すでにDatagridViewのdatasourceが指定してあるので不要と考えたのですが、これが間違いですか?

    注:親フォーム、子フォームの表現を使いましたが、便宜上です。 コードでは、その指定はしていません。

    YKsaila


    • 編集済み yksaila 2015年5月31日 0:48
    2015年5月31日 0:02
  • 要点を整理すると以下ですか?

    1. MenuFormのボタンクリックイベントでDetailFormのインスタンスを作成
    2. この時点ではDetailFormのDataGridView.DataSourceは未設定
    3. DetailFormをモーダル表示
    4. DetailFormのForm_LoadでDataTableをバインド
    5. DetailFormのDataGridView上のセルを選択 → 編集状態にならない

    誰でも再現取れるミニマムコードをご提示いただいたほうが解決が早い気がします。
    例えば、上の要点では以下のようになるでしょう。

    Public Class MenuForm
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim frm As New DetailForm
            frm.Show()
        End Sub
    End Class
    Public Class DetailForm
        Private Sub DetailForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            DataGridView1.Columns.Add(New DataGridViewTextBoxColumn() With
                                      {
                                          .HeaderText = "test",
                                          .DataPropertyName = "test"
                                      })
            Dim dt As New DataTable()
            dt.Columns.Add("test")
            dt.Rows.Add("hoge")
            DataGridView1.DataSource = dt
        End Sub
    End Class

    当然ですが、ご質問にあった問題は上記コードでは発生しません。
    他に何か要因があるのだと思われますが、質問者様のコード全体からでないとわからない問題かもしれません。

    あと、環境を記載してください。(.NET Frameworkのバージョンなど)

    2015年5月31日 2:50
    モデレータ
  • yksaila さま 拝見しました。

    F.Show() と後続する値の代入が問題なのでは。
    値の代入を F (子)の中でするように変更すればいいと思います。
    環境を子の中で準備するなら、全ての処理をその中でするべきだと思います。

    逆に、呼び出し側で準備するのなら、その中で。

    その辺りが、挙動を不定にしている(実際には書いてある通りの挙動になっている)と思います。

      ( F を用意した時の値が反映されていて、 F を表示した後の変化は反映されていない のでは?。 )

    • 編集済み ShiroYuki_Mot 2015年5月31日 3:22 括弧書き追記
    2015年5月31日 3:13
  • 環境は、以下のとおりです。

    クライアントPC--Win.7 professional  ;サーバー--別マシーン(Win server 2003R2 standard edition);VB 2010 Express

    すいません、うっかりしていました! 以下の状況でした。

    いつも、Datagridviewの一行目で実験していました---二行目以降もしているつもりでしたが(初回入力)、実はそうではなかったです!

    実態は以下の通りです;今、気づきました。

    二行目以降に初回入力するときはきちんと表示しています。 一行目への初回入力だけ、失敗します(二回目以降は表示)。

    なぜなのでしょうか?

    ご回答・検討の方向が変わってしまい、ご迷惑をおかけしました、申し訳ありません。

    十分に実験していたつもりでしたが。

    YKsaila

    2015年6月1日 23:52
  • Tak1Wa様への返信にも書いたのですが、以下の通りです。

    すいません、うっかりしていました! 以下の状況です。

    Datagridviewの一行目で実験していました---二行目以降もしているつもりでしたが(初回入力)、実はそうではなかったです!

    実態は以下の通りです;今、気づきました。

    二行目以降に初回入力するときはきちんと表示しています。 一行目への初回入力だけ、失敗します(二回目以降は表示)。

    なぜなのでしょうか?

    ご回答・検討の方向が変わってしまい、ご迷惑をおかけしました、申し訳ありません。

    十分に実験していたつもりでしたが。

    YKsaila

    2015年6月1日 23:54
  • 1行目がダメで2行目がOKということですから、それらの違いで思い付くのはCurrent行であるかどうかです。子フォームを開いた際にCurrent行はどうなっていますか? 何かコードやデザイナで、Currnet行やCurrentセルが変るような設定をされていますか?
    試しに、子フォームを開いた直後に、Current行、もしくはCurrentセルを一度他に設定し、また元にCurrentを戻してみると良いかもしれません。これを行うには、プロジェクトをコピーして行えば、オリジナルのプロジェクトに傷を付けずに済みます。
    また、このようにコピーしたプロジェクトにおいて、問題が発生しなくなるまでコードを削って行けば、原因のコードが特定できるかもしれません。もし問題が発生し続けても、プロジェクトは十分に小さなものになっているはずですから、それをこの場でご提示いただくと、解決が早いかも知れません。


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2015年6月2日 4:50
    モデレータ
  • 'コンボ・クリックが二回なのを、一回で済ませる!--- SendKeys.SendWait("{F4}") > SendKeys.Send("{F2}")
    If Me.DataGridView1.Columns(e.ColumnIndex).Name = "CB_ItemName" _
               AndAlso TypeOf Me.DataGridView1.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then
         SendKeys.SendWait("{F4}")
    End If


    上記の部分は SendKeys を使わずとも、こんな感じで処理できるかも。

    ’CellClick イベントにて
    Dim grd = DirectCast(sender, DataGridView)
    If e.RowIndex >= 0 AndAlso e.ColumnIndex >= 0 AndAlso _
      TypeOf grd.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then
        grd.BeginEdit(True)
        DirectCast(grd.EditingControl, ComboBox).DroppedDown = True
    End If

    2015年6月2日 6:41
  • 原因が分かったかもしれません。 思いもよらないところでした!

    明細部(DataGridView1)のID(Scrap_ID)を非表示にすると、一行目が初回クリックで編集モードになりません。
    二回目のクリックでは成功しますが(二行目以降は、初回クリックでも成功します)。

    ここ(Scrap_ID)を表示にすると、上記の現象は起こりません。


    Form_Load内で、下記のように設定しています:
    With Me.DataGridView1
     ---略---

      .Scrap_ID.Visible = False  --- ここが原因のようです。

     ---略---
    End With


    .Scrap_ID.Visible = False を「なし」にします。
    ただ、修正画面なので.Scrap_ID.ReadOnly = Trueは、必要になりますが。


    フォームの設定内容
    フォーム(F_I_Scrap)---サーバー上のテーブルは、T_Scrap(主キー:Scrap_ID)
    このフォームの明細部(DataGridView)---サーバー上のテーブルは、T_ScrapDetails(主キー:Scrap_ID & LineNo)


    フォーム画面上には、ID(Scrap_ID)を表示するテキストボックスが設けてあります。
    明細部にもこのIDを表示するのは、見た目でもうるさいので、非表示にしていました。
    これを表示に戻すと、ここでの問題事象は発生しません。

    これは、VB上の仕様なのでしょうか?
    だとしたら、仕方のないことですが。

    したがって、解決策は二通りあります(現段階での私では;)。
    (1)明細部の各行でも、IDを表示する <見た目で少しうるさいが、我慢する>。
    (2)親フォーム(?)のコマンドボタンからこのフォームを表示するわけですが、
      この段階でF.Showだけでなく、FのDataGridViewのデータソースも指定しておく(F:F_I_Scrap)。
        つまり、FのForm_Load内でのDataGridViewのデータソース指定だけでは、不十分であるようです。

    ほかに解決策はあるのでしょうか?ダメなら、(2)のやり方で進めれば済むことですが。

    YKsaila

    2015年6月4日 0:41
  • > ほかに解決策はあるのでしょうか?

    他の回答者の方が書かれていますが、コピペすれば現時点の問題を再現できる必要最低限のコードをアップして、再現手順を書くことはできませんか?

    2015年6月4日 3:43
  • 6/4 17:55 編集

    前回アップしたものを削除して、もっと簡単にしたものをアップしました。

    さすがに、以前のは分かりにくいです(作成した本人以外には)、申し訳ありませんでした。

    今度のものは再現しやすいと思います。

    且つ、原因らしき箇所は二か所ということが判明。

     '****(1)と '****(2)です。 どちらかを「なし」にすれば、正常になりました。

    DataGridView1.CellValidatingにおける、CurrentRowに問題があるのかも?

    まだ、Currentになっていないということでしょうか?

    (二行目以降入力は、初回クリックでもOK、また列をソートしてからだと、一行目・初回クリックでもOKですが)

    YKsaila

    以下コードです;

    Private Sub DataGridView1_CellValidating(ByVal sender As Object, ByVal e As DataGridViewCellValidatingEventArgs) Handles DataGridView1.CellValidating
            On Error GoTo ErrorHandle
            '----略---

            '****(1)
            Me.DataGridView1.CurrentRow.Cells("Amount").Value = CInt((Me.DataGridView1.CurrentRow.Cells("Volume").Value)) * _
            CInt(Me.DataGridView1.CurrentRow.Cells("UnitPrice").Value)

            '----略---
            Exit Sub  '必要!!
    ErrorHandle:
            MsgBox(Err.Number & ": " & Err.Description)
    End Sub

    Private Sub F_I_Scrap_New_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            On Error GoTo ErrorHandle
          
            'SQL認証接続
            Dim St As String
            Dim Con As New System.Data.SqlClient.SqlConnection
            Dim SQL As System.Data.SqlClient.SqlCommand
            Dim ServerName As String = GC_SVRName   'サーバー名(またはIPアドレス)
            Dim UserID As String = GC_UserID      'ユーザーID
            Dim Password As String = GC_Password   'パスワード
            Dim DatabaseName As String = GC_DatabaseName
            St = "Server=" & ServerName & ";"
            St &= "User ID=" & UserID & ";"
            St &= "Password=" & Password & ";"
            St &= "Initial Catalog=" & DatabaseName
            Con.ConnectionString = St
            SQL = Con.CreateCommand
            Con.Open()
            Dim adapter = New SqlClient.SqlDataAdapter()

            Dim SCID As Integer
            SCID = NZint(Me.TX_Scrap_ID.Text)
          
            Dim sb2 As New System.Text.StringBuilder
            Dim ds_ScrapDetails = New DataSet
            Dim dt_ScrapDetails As New DataTable  'DataGridView1のデータテーブル(DataSource)
            adapter.SelectCommand = SQL
          
            sb2.Append("SELECT T_ScrapDetails.Scrap_ID")  
            sb2.Append(" ,T_ScrapDetails.[LineNo]")      'LineNoは、Sql Serverの予約語、このままでは使用できない。[--]を使用する。
            sb2.Append(" , T_ScrapDetails.Volume")    
            sb2.Append(" , T_ScrapDetails.UnitPrice")     
            sb2.Append(" , T_ScrapDetails.Amount")     
            sb2.Append(" FROM T_ScrapDetails")
            sb2.Append(" WHERE T_ScrapDetails.Scrap_ID= @Scrap_ID order by [LineNo]")
            SQL.Parameters.AddWithValue("@Scrap_ID", SCID)
            SQL.CommandText = sb2.ToString()
            adapter.SelectCommand = SQL
            adapter.Fill(ds_ScrapDetails)
            dt_ScrapDetails = ds_ScrapDetails.Tables(0)

            '▼DataGridView1の値の表示---F_I_Scrapの中のDataGridView1の内容表示---- 'DataGridView1のデータテーブル(DataSource)=dt_ScrapDetails
            DataGridView1.DataSource = dt_ScrapDetails
            'クリック一回で表示させる?!
            DataGridView1.BeginEdit(True)

            '列名(英語--サーバー)を日本語表記にして表示
            With DataGridView1

                .Columns("Scrap_ID").Visible = False  '****(2)
               
                .Columns("LineNo").ReadOnly = True

            End With


            Con.Close()
            adapter.Dispose()
            dt_ScrapDetails.Dispose()
            SQL.Dispose()
            Con.Dispose()

            Exit Sub  '必要!!
    ErrorHandle:
            MsgBox(Err.Number & ": " & Err.Description)
    End Sub


    • 編集済み yksaila 2015年6月4日 9:04
    2015年6月4日 5:33
  • せっかくコードをアップいただいたのに何ですが・・・

    SQL Server データベースがないと動かないようですし、問題とは関係なさそうな余計なコードが多々あるようですし、先にお願いした「コピペすれば現時点の問題を再現できる必要最低限のコード」ではないですよね?

    どうしても SQL Server を使う必要があるなら Northwind とか AdventureWorks など誰でも入手可能なサンプルデータベースを使用し、問題を再現するのに関係ないコードはどんどん削っていって(どんどん削るというのが非常に重要なところです)、ホントに必要最低限のコードをアップするということはできませんか?

    そうしていただけないと、ここに書いてあること以外は何も知り得ない回答者・閲覧者がフォローするのは難しいです。

    現状のまま話を進めたいということであれば、すみませんが自分はお役に立てそうもありませんので、他の方の回答に期待してください。

    2015年6月4日 6:55
  • 私もSurferOnWwwさんに同意です。
    今の状態ですと、「どんどん削る」の部分を回答者側がトライエラーする必要があり、時間がかかってしまいそうです。

    もう少し小さくなるとありがたいです。というのと、
    以下のようにとりあえず試してみたのですが、やはり問題は起きませんでした。

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
        Dim dt As New DataTable()
        With dt
            .Columns.Add("hoge1")
            .Columns.Add("hoge2")
            .Columns.Add("hoge3")
            .Rows.Add("A1", "B1", "C1")
            .Rows.Add("A2", "B2", "C2")
            .Rows.Add("A3", "B3", "C3")
        End With
    
        With DataGridView1
            .Columns.Add(New DataGridViewTextBoxColumn() With {.Name = "hoge1", .DataPropertyName = "hoge1"})
            .Columns.Add(New DataGridViewTextBoxColumn() With {.Name = "hoge2", .DataPropertyName = "hoge2"})
            .Columns.Add(New DataGridViewTextBoxColumn() With {.Name = "hoge3", .DataPropertyName = "hoge3"})
    
            'プロパティが何か影響している?→してなさそう
            .AllowUserToAddRows = True
            .AllowUserToDeleteRows = True
    
            'ソートモードが関係してる?→してなさそう
            For Each c As DataGridViewColumn In .Columns
                c.SortMode = DataGridViewColumnSortMode.NotSortable
            Next
    
            'ReadOnlyまたはVisibleが関係している?→してなさそう
            .Columns("hoge1").ReadOnly = True
            .Columns("hoge2").Visible = False
            .Columns("hoge2").ReadOnly = True
    
            .DataSource = dt
        End With
    
    End Sub
    

    ReadOnly列が結構多いので、何かが原因で編集したいエリアがReadOnlyとして扱われているのかも?とは思ったのですが…
    DB周りはおそらく関係無いと思うのです。今回のケースでは。(勘)

    2015年6月4日 7:26
    モデレータ
  • >「コピペすれば現時点の問題を再現できる必要最低限のコード」ではないですよね?

    そうですね、おっしゃる通りです。 すいません。

    個人的にはどこに問題があるのか特定できないので、こうしたのですが(可能性のありそうな部分を残しました)。

    ただ、どこに原因があるのかは分かりましたので(前の書き込みをご覧ください)、問題はなぜそうなのか、だけです。

    6/4 17:55 追加書き込み

    以前のものを削除し、編集し直しました。ご覧ください。

    YKsaila


    • 編集済み yksaila 2015年6月4日 8:55
    2015年6月4日 7:53
  • 現在の質問は、

    > ほかに解決策はあるのでしょうか?

    でしたよね? その質問に答えるには「コピペすれば現時点の問題を再現できる必要最低限のコード」を示していただかないと、少なくとも自分には無理です。

    また、上のレスの質問「なぜそうなのか」も分かりません。

    2015年6月4日 8:24
  • おっしゃる通りです。すいませんでした。

    以前のものを削除し、編集し直しました。ご覧ください。

    YKsaila

    2015年6月4日 8:52
  • おっしゃる通りです。すいませんでした。

    以前のものを削除し、編集し直しました。ご覧ください。原因らしきものも特定できたと思うのですが?

    YKsaila

    2015年6月4日 8:54
  • おっしゃる通りです。すいませんでした。

    以前のものを削除し、編集し直しました。ご覧ください。原因らしきものも特定できたと思うのですが?

    YKsaila

    2015年6月4日 8:56
  • > 以前のものを削除し、編集し直しました。

    それ、絶対に止めてください。検索などで後からここのスレッドを訪れた人が、ここを上から順番に読んでいったら話がつながらないですよね。

    ここは質問者さん専用の Q&A サイトではなく、以下のページに書いてあるように「ユーザー同士が情報交換を行うための場」です。それを考えたら、そういうことをするのは趣旨に反するのはお分かりいただけると思いますが。

    フォーラムでご質問頂くにあたっての注意点
    https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?forum=announceja

    > 原因らしきものも特定できたと思うのですが?

    いえ、前に出てなかったコードも出てきて、ますますわかりません。「コピペすれば現時点の問題を再現できる必要最低限のコード」というのは無理なんでしょうか?

    2015年6月4日 11:45
  • > 以前のものを削除し、編集し直しました。

    >それ、絶対に止めてください。検索などで後からここのスレッドを訪れた人が、ここを上から順番に読んでいったら話がつながらないですよ>>ね。

    >ここは質問者さん専用の Q&A サイトではなく、以下のページに書いてあるように「ユーザー同士が情報交換を行うための場」です。それを>>考えたら、そういうことをするのは趣旨に反するのはお分かりいただけると思いますが。

    ご迷惑をおかけして、申し訳ありません。 ただ、私の言い分を述べさせてください。

    「フォーラム質問での注意」については分かっていたのですが、編集前のものはあまりにも複雑でしたし、無関係な余分なものが多くかえって皆さんが混乱すると思った次第です。

    編集後の方が元のものに比べて、無関係なものがなくなっていますので、スレッドを訪れた人にとっても「良い」と判断しました。 

    <本質的には全く元と変わっていません。> 

    このスレッドを訪問する方にとっても、話のつながりは絶対に壊れていないはずです。 より簡略化しており、再現しやすいとおもったわけです。

    >前に出てなかったコードも出てきて、ますますわかりません。「コピペすれば現時点の問題を再現できる必要最低限のコード」というのは>無理なんでしょうか?

    より簡略化することで、Form_Loadのコードと、CellValidatingのコードとの関係も分かってきました。

    この二個のコードで、再現できると考えています。 

    YKsaila


    • 編集済み yksaila 2015年6月4日 14:53
    2015年6月4日 13:55

  • Handles DataGridView1.CellValidatingに問題があるようです。
     '****(1)以下の二行のコードを、DataGridView1.CellValidatedに入れ替えると、問題は解決します( DataGridView1.CellValidatingからは、'****(1)以下の二行のコードは削除)

     '****(1)のコード:
             Me.DataGridView1.CurrentRow.Cells("Amount").Value = CInt(Me.DataGridView1.CurrentRow.Cells("Volume").Value) * _
             CInt(Me.DataGridView1.CurrentRow.Cells("UnitPrice").Value)


    私の推測:
    当然、CellValidatingは、CellValidatedより前に起こる事象です。

    で、たぶん、CellValidatingでのCurrentRowは、第一行なのでしょう。

    で、一行目の数量欄(Volume)に数値を入れても、'****(1)が優先されてこの数値入力が反映されないと考えました。
    CInt(Me.DataGridView1.CurrentRow.Cells("Volume").Value)が優先されるのではないでしょうか?

    一行目の数量欄(Volume)に、初回数値を入れても(CellValidated)、まだCellValidatingの影響が残っているのでは、と考えています。
    それだけ、CellValidatingは強力なのかも?
    間違いかもしれませんが。


    '****(1)のコードを、.RowValidatingに入れ替えてもうまくいきます。

    ちなみに、'****(1)のコードを、.CellValidating / .CellValidated / .RowValidatingの全部に入れると、先の問題事象が発生します。
    .CellValidatingが影響しています。

    .CellValidatingから、'****(1)のコードを削除すれば、問題は解決します(.CellValidated / .RowValidatingの両方に残してもOKです。)

    YKsaila
    • 回答としてマーク yksaila 2015年6月4日 14:49
    • 編集済み yksaila 2015年6月4日 14:57
    2015年6月4日 14:48
  • このスレッドを検索でご覧になる方の為に書き込みます。

    ご質問の内容に対し、いずれの返信者さんもが想像推測し得なかった回答をご質問者ご自身がしました。
    つまり、当初、関係が無かったと思っていた所に、糸口があった事になります。

    さて、DataGridView でセルを Validating する時には、その名の如く、セルを指定して各々の処理を書く事になります。

    今回の結末や途中経過に CellValidating の条件分岐部分が提示されていないので、以下は推測ですが、
    対象セルを含む代入や値の参照は留意すべき点があります。
    それは、対象セルは Validating 時点では入力値に変更されていない事です。
    EditingControl でアクセスしなければいけません。

    仮に、この推測があっているとすれば、一回目とそれ以降が結果が違う は事象を掴みきれていない可能性もあります。
    つまり、ひとつ前の状態を拾っている可能性を、私は、捨てきれません。
    2015年6月5日 0:58
  • 追加の書き込みを数日迷ったのですが、書きこむことにしました。

    回答者の皆様には全く不要でしょうが、訪問者の方には何かのお役には立つのでは? と考えました。

    あれから、再度の実験をしてみました:

    Form_Load,CellValidating,CellValidated,RowValidating以外のコードは、フォームから全て削除して、コードを簡略化しました。。
    Form_Load,CellValidating,CellValidated,RowValidating内のコードも、上記記載のコードのみとして余分なものは削除しました。

    これで、実験の環境は整います。

    (1)CellValidating実験:CellValidated,RowValidating内のコードは、コメント行に変更して実験。
    Form_Load内のDataGridViewでの、Scrap_ID=非可視は無しにしておきます(DataGridView1.Columns("Scrap_ID").Visible = Trueにする)。

    CellValidatingは、入力前の数値で動きます。例の計算式がCellValidating内にあることは、本来は意味をなさないです。

    実験(例):フォーム表示時は、Volume=3, UnitPrice=25, Amount=75(:Volume * UnitPrice)とします。
    ここで、Volume欄に「10」入力 → Amount=75のままです(250には、ならない)。
    再度10を入力すると、250になる(この入力された10でなく、前回の10が使用されている)。 
    この二度目の10入力の時、10でなく4を入力してみても、Amount=250 (100にはならない)。

    このことから、CellVlidatingでは、入力前の数値を使用していることが分かります。

    元のフォームのコードは、4,000行前後あり、何かの関係でCellValidatingに入ってしまったらしい。
    かつ、それはCellValidated内にもあったので、異常に気付かなかったものです。

    しかし、計算式でなくとも、何らかの形でCellValidating内に列名があると、ここでの問題は発生します。
    以下参照:

    DataGridView, 一行目、初回入力失敗関連---

    (2)CellValidating内の計算式コードは、コメント行にして、別のコードを入れる。
     かつ、Form_Load内のDataGridViewでの、Scrap_ID=非可視にする(DataGridView1.Columns("Scrap_ID").Visible = False)。

         Private Sub DataGridView1_CellValidating(ByVal sender As Object, ByVal e As DataGridViewCellValidatingEventArgs) Handles DataGridView1.CellValidating
            On Error GoTo ErrorHandle
            'Me.DataGridView1.CurrentRow.Cells("Amount").Value = CInt(Me.DataGridView1.CurrentRow.Cells("Volume").Value) * _
            'CInt(Me.DataGridView1.CurrentRow.Cells("UnitPrice").Value)
            '以下意味のないコードですが、実験用です。
            Me.DataGridView1.CurrentRow.Cells("Amount").Value = Me.DataGridView1.CurrentRow.Cells("Amount").Value
            Me.DataGridView1.CurrentRow.Cells("Volume").Value = Me.DataGridView1.CurrentRow.Cells("Volume").Value
            Me.DataGridView1.CurrentRow.Cells("UnitPrice").Value = Me.DataGridView1.CurrentRow.Cells("UnitPrice").Value

            Exit Sub  '必要!!
    ErrorHandle:
            MsgBox(Err.Number & ": " & Err.Description)
        End Sub

    Form_Load内でのDataGridView
            With DataGridView1
                .Columns("Scrap_ID").Visible = False
            End With

    こうやっても、例の問題事象は発生します。一行目、初回入力失敗!
    Scrap_IDを可視にすれば、発生しません(DataGridView1.Columns("Scrap_ID").Visible = True)。
    これが、主キーであることは無関係です----主キーをはずして実験済。


    (3)DataTable(DataSource)の第一列がScrap_IDであることと、関係があることを発見。
     DataGridView1.Columns("Scrap_ID").Visible = Falseのままで、datatableの第一列のScrap_IDを後ろのまわします。

    つまり、下記のようにします。
     sb2.Append("SELECT T_ScrapDetails.Volume") 
     sb2.Append(" ,T_ScrapDetails.[LineNo]")     
     sb2.Append(" , T_ScrapDetails.UnitPrice")   
     sb2.Append(" , T_ScrapDetails.Amount")    
     sb2.Append(" , T_ScrapDetails.Scrap_ID")

    こうすれば、問題は発生しません。

    しかし、このとき、DataGridView1.Columns("Volume").Visible = Falseとすれば、問題発生。
    DataTable(DataSource)の第一列.Visible = Falseとすれば、問題が発生するようです。


    CellValidatingのコード内に列「A」の記載があり、
    かつDataGridViewのDataTable(DataSource)の第一列を、DataGridView1.Columns("A").Visible = Falseとすれば、問題が発生するのかもしれません。


    <解決策>
    ①Scrap_ID=可視にすれば良い。
    ②Scrap_ID=非可視にしたいのであれば、このIDを、DataGridViewのDataTable(DataSource)の第一列に持ってこなければよい(列順変更)。
    ③親フォームから、子フォームのDataGridViewのDataTable(DataSource)をあらかじめ指定する。

    ②が良いように思います。

    原因は、DataGridViewのDataTable(DataSource)の仕様とCellValidatingが関わっているようですが、詳細については不明です。





    • 編集済み yksaila 2015年6月15日 20:48
    2015年6月15日 20:41