datagridview 最后一个以编程方式更改的单元格未包含在更改的行中

datagridview last programmatically changed cell does not get included in changedrows

VB.net 应用程序以编程方式更改数据网格视图中的值。这些值都是它们应该的,但是保存例程(dtShipments 是作为 datagridview 源的数据表) 将 dtChanges 变暗为 DataTable = dtShipments.getchanges() 如果更改了多个,dtChanges 总是缺少最后一行。

在更改单元格值的例程中,我尝试了 DatagridView1.EndEdit 和 DatagridView1.CommitEdit,但行为是相同的。我什至尝试添加 SendKeys.Send(vbTab) 行,因为在手动进行更改时按下 Tab 键足以让所有更改显示在 .GetChanges 中。

我错过了什么?

每个请求的代码:

Private Sub btnAssign_Click(sender As Object, e As EventArgs) Handles btnAssign.Click
        strModErr = "Form1_btnAssign_Click"
        Dim sTruck As String = ""
        Try
            sTruck = Me.DataGridView2.SelectedCells(0).Value.ToString
            For Each row As DataGridViewRow In DataGridView1.SelectedRows
                row.Cells("Truck").Value = sTruck                
            Next
            DataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit)
        Catch ex As Exception
            WriteErrorToLog(Err.Number, strModErr + " - " + Err.Description)
        End Try
    End Sub
Private Function SaveChanges() As Boolean
        strModErr = "Form1_SaveChanges"
        Dim Conn As New SqlConnection(My.Settings.SQLConnectionString)
        Dim sSQL As String = "UPDATE fs_Shipments SET Truck = @Truck, Stop = @Stop WHERE SalesOrder = @SO"
        Dim cmd As New SqlCommand(sSQL, Conn)
        Dim sSO, sTruck As String
        Dim iStop As Integer = 0
        Try
            DataGridView1.EndEdit()
            DataGridView1.ClearSelection()
            Dim dtChanges As DataTable = dtShipments.getchanges()       'DataRowState.Modified

            If Not dtChanges Is Nothing Then
                Conn.Open()

                For Each row As DataRow In dtChanges.Rows
                    sSO = row("SalesOrder").ToString
                    sTruck = row("Truck").ToString
                    iStop = CInt(row("Stop").ToString)
                    With cmd.Parameters
                        .Clear()
                        .AddWithValue("@SO", sSO)
                        .AddWithValue("@Truck", sTruck)
                        .AddWithValue("@Stop", iStop)
                    End With
                    cmd.ExecuteNonQuery()
                Next
            End If
            Return True
        Catch ex As Exception
            WriteErrorToLog(Err.Number, strModErr + " - " + Err.Description)            
            Return False
        End Try
    End Function

我不是 100% 确定为什么会这样。该问题似乎特定于用户“选择”第一个网格中的单元格,然后调用 SaveChanges 代码“之前”用户在第一个网格中进行了另一个选择。换句话说,如果用户在网格 1 中“选择”要将卡车“分配”到的行,则“在”“选定”单元格已被选定卡车填满后,然后,用户在网格 1 中选择其他单元格网格 1,然后调用保存更改代码。在这种情况下,代码会按预期工作。

我所有提交更改的尝试都失败了或引入了其他问题。我相信这在很大程度上与网格 SelectedRows 集合有关。恕我直言,这看起来是一种设置单元格值的冒险方式,我认为更 user-friendly 的方法可能是在每一行上添加一个复选框并将卡车值分配给选中的行。但这是一个意见。

无论如何,经过多次尝试,我想出的解决方案只是进一步证明了为什么使用 BindingSource 在很多方面都很有用。在这种情况下,DataTable 似乎没有随着最后一次单元格更改而更新。同样,我不确定这是“为什么”,但是由于它似乎可以使用 BindingSource,我只能假设它与 DataTable 本身有关。换句话说,在执行“Save”代码之前,我们可以调用 tables AcceptChanges 方法,但是我们会丢失这些更改。所以那不是一个选择。

为了提供帮助,下面是一个完整的 (no-fluff) 示例。将来,我强烈建议您提取与问题无关的代码部分。例如,所有将更改保存到数据库的代码相对于问题都是多余的……因此将其删除。添加到问题中的不必要代码越多,只会增加“忽略”该问题的 SO 用户的数量。如果您 post 重现问题的最小、完整和有效的代码,以便用户可以简单地复制和粘贴而无需添加附加代码或删除不必要的代码,将会增加您获得好的答案的机会。只是为未来提个醒。

下面的解决方案只是添加了一个BindingSourceBindingSource’s DataSource 就是DataTable dtShipments 然后BindingSource 就用了DataSourceDataGridView1。然后在 SaveChanges 方法中,“在”代码调用 dtShipment.GetChanges() 方法之前,我们将调用 BindingSources.ResetBinding() 方法来完成对基础数据源的编辑,在本例中 DataTable dtShipments.

如果您创建一个新的 VS-VB winforms 解决方案,请将两 (2) 个网格和两 (2) 个按钮拖放到窗体上,如下所示,然后将按钮名称更改为“btnAssign”和“btnApplyChanges”。 ”分配按钮会将网格二中的选定卡车添加到网格一中的选定行。应用更改按钮只是重置绑定源并显示一个消息框,其中包含 dtShipments DataTable 中更改的行数。应该注意的是,代码调用 dtShipments.AcceptChanges() 方法来清除更改。否则,代码将更新已经进行的更改。

Dim dtShipments As DataTable
Dim dtTrucks As DataTable
Dim shipBS As BindingSource

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  dtShipments = GetShipmentsDT()
  shipBS = New BindingSource()
  shipBS.DataSource = dtShipments
  dtTrucks = GetTrucksDT()
  dtShipments.AcceptChanges()
  DataGridView1.DataSource = shipBS
  DataGridView2.DataSource = dtTrucks
End Sub

Private Function GetShipmentsDT() As DataTable
  Dim dt = New DataTable()
  dt.Columns.Add("ID", GetType(String))
  dt.Columns.Add("Truck", GetType(String))
  dt.Rows.Add(1)
  dt.Rows.Add(2)
  dt.Rows.Add(3)
  dt.Rows.Add(4)
  Return dt
End Function

Private Function GetTrucksDT() As DataTable
  Dim dt = New DataTable()
  dt.Columns.Add("Truck", GetType(String))
  For index = 1 To 10
    dt.Rows.Add("Truck" + index.ToString())
  Next
  Return dt
End Function

Private Sub btnAssign_Click(sender As Object, e As EventArgs) Handles btnAssign.Click
  Dim sTruck = DataGridView2.SelectedCells(0).Value.ToString
  'Dim drv As DataRowView
  For Each row As DataGridViewRow In DataGridView1.SelectedRows
    'drv = row.DataBoundItem
    'drv("Truck") = sTruck
    row.Cells("Truck").Value = sTruck
  Next
  'DataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit)
  'shipBS.ResetBindings(True)
  'DataGridView1.CurrentCell = DataGridView1.Rows(0).Cells(0)
End Sub

Private Function SaveChanges() As Boolean
  Try
    shipBS.ResetBindings(True)
    Dim dtChanges As DataTable = dtShipments.GetChanges()
    If (dtChanges IsNot Nothing) Then
      MessageBox.Show("There are " & dtChanges.Rows.Count & " rows changed in the data table")
      ' update sql DB
      dtShipments.AcceptChanges()
    End If
    Return True
  Catch ex As Exception
    Return False
  End Try
End Function

Private Sub btnApplyChanges_Click(sender As Object, e As EventArgs) Handles btnApplyChanges.Click
  Dim ChangesMade As Boolean
  ChangesMade = SaveChanges()
End Sub

最后,由于您使用的是 VB... 我强烈建议您将“严格”选项设置为“开”。当此选项打开时,它将标记您可能遗漏的可能错误。要为所有未来的 VB 解决方案执行此操作,请打开 VS,关闭所有可能打开的解决方案,然后转到工具->选项->项目和解决方案->VB 默认值,然后设置“Option Strict ”到“开”。默认设置为关闭。

我希望这是有道理的并且有所帮助。