如何制作触发循环的datagridview cellvaluechanged 2列

how to make a datagridview cellvaluechange 2 column which trigged loop

我正在开发销售订单应用程序。我正在使用数据网格视图来填写销售订单。

我的数据网格视图中的字段如下所示

物品代码 - 物品描述 - 数量 - 价格

描述字段是一个组合框。

我想要的是当用户输入 ItemCode 时,它​​会自动检查我的数据库并给我 Itemdescription

我还希望用户能够 select ItemDescription 中的一个项目,它是一个组合框,它会自动更新我的 Itemcode。

    Private Sub salesorder_dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles salesorder_dgv.CellValueChanged
    If salesorder_dgv.Rows.Count > 0 Then
        If e.ColumnIndex = 0 Then
            Dim READER As SqlDataReader
            conn.Open()
            Dim query As String
            query = "select * from item where code = '" & salesorder_dgv.Rows(e.RowIndex).Cells(0).Value & "'"
            cmd = New SqlCommand(query, conn)
            READER = cmd.ExecuteReader
            If READER.Read Then
                salesorder_dgv.Rows(e.RowIndex).Cells(1).Value = READER.GetString(2)
            End If
            conn.Close()
        End If

        If e.ColumnIndex = 1 Then
            Dim READER As SqlDataReader
            conn.Open()
            Dim query As String
            query = "select * from item where description = '" & salesorder_dgv.Rows(e.RowIndex).Cells(1).Value & "'"
            cmd = New SqlCommand(query, conn)
            READER2 = cmd.ExecuteReader
            If READER.Read Then
                salesorder_dgv.Rows(e.RowIndex).Cells(0).Value = READER.GetString(1)
            End If
            conn.Close()
        End If
    End If
End Sub

有没有办法让这段代码起作用?我收到“连接未关闭”

那里有很多错误,所以我将首先解决您必须清理的问题,然后解决您应该如何做的问题。

如评论中所建议,您应该在需要的地方创建所有 ADO.NET 对象,包括连接。您在尽可能小的范围内创建、使用和销毁。此外,如果您只需要单个列中的数据,请不要使用 SELECT *。仅检索您需要的列。由于您只从一行的一列中检索数据,因此您应该使用 ExecuteScalar 而不是 ExecuteReader.

接下来,您应该熟悉 DRY 原则,即不要重复自己。您那里有两个几乎相同的代码块,因此您应该提取出公共部分并只编写一次并传入不同的部分。

最后,不要使用字符串连接将值插入 SQL 代码。始终使用参数。它避免了许多问题,最重要的是 SQL 注入,这在您的情况下是很有可能的,因为用户正在输入自由文本。考虑到所有这些,您拥有的代码将像这样重构:

Private Sub salesorder_dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles salesorder_dgv.CellValueChanged
    If salesorder_dgv.RowCount > 0 Then
        Dim sourceColumnIndex = e.ColumnIndex
        Dim targetColumnIndex As Integer
        Dim query As String

        Select Case sourceColumnIndex
            Case 0
                targetColumnIndex = 1
                query = "SELECT description FROM item WHERE code = @param"
            Case 1
                targetColumnIndex = 0
                query = "SELECT code FROM item WHERE description = @param"
            Case Else
                Return
        End Select

        Dim row = salesorder_dgv.Rows(e.RowIndex)
        Dim sourceValue = row.Cells(sourceColumnIndex).Value

        Using connection As New SqlConnection("connection string here"),
              command As New SqlCommand(query, connection)
            command.Parameters.AddWithValue("@param", sourceValue)
            connection.Open()

            row.Cells(targetColumnIndex).Value = command.ExecuteScalar()
        End Using
    End If
End Sub

现在来看看你应该怎么做。如果您使用所有描述填充组合框列,那么您必须首先在数据库中查询它们。您应该做的是检索初始查询中的描述和代码。这样,您就不必返回数据库。您可以使用代码和描述填充 DataTable,然后大部分工作都会为您完成。

对于下面的示例,我首先在设计器中设置表单,这意味着在网格中添加和配置适当的列并添加 BindingSource 组件。这还包括设置每个网格列的 DataPropertyName 属性 以便它绑定到适当的源列。我也在此处手动填充项目数据,但您将从数据库中获取该数据。

Private itemTable As New DataTable

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    LoadItemData()
    LoadSaleData()
End Sub

Private Sub LoadItemData()
    With itemTable.Columns
        .Add("code", GetType(String))
        .Add("description", GetType(String))
    End With

    With itemTable.Rows
        .Add("123", "First Item")
        .Add("abc", "Second Item")
        .Add("789", "Third Item")
        .Add("xyz", "Fourth Item")
        .Add("01a", "Fifth Item")
    End With

    itemBindingSource.DataSource = itemTable

    With itemDescriptionColumn
        .DisplayMember = "Description"
        .ValueMember = "Description"
        .DataSource = itemBindingSource
    End With
End Sub

Private Sub LoadSaleData()
    Dim saleTable As New DataTable

    With saleTable.Columns
        .Add("ItemCode", GetType(String))
        .Add("ItemDescription", GetType(String))
        .Add("Quantity", GetType(Integer))
        .Add("Price", GetType(Decimal))
    End With

    saleBindingSource.DataSource = saleTable
    salesorder_dgv.DataSource = saleBindingSource
End Sub

Private Sub salesorder_dgv_CellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs) Handles salesorder_dgv.CellValidating
    If e.RowIndex >= 0 AndAlso
       e.ColumnIndex = 0 AndAlso
       Not String.IsNullOrEmpty(e.FormattedValue) Then
        'Check that the code entered by the user exists.
        e.Cancel = (itemBindingSource.Find("code", e.FormattedValue) = -1)

        If e.Cancel Then
            MessageBox.Show("No such item")
        End If
    End If
End Sub

Private Sub salesorder_dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles salesorder_dgv.CellValueChanged
    Dim rowIndex = e.RowIndex
    Dim sourceColumnIndex = e.ColumnIndex

    If rowIndex >= 0 And sourceColumnIndex >= 0 Then
        Dim sourceColumnName As String
        Dim targetColumnName As String
        Dim targetColumnIndex As Integer

        Select Case sourceColumnIndex
            Case 0
                sourceColumnName = "code"
                targetColumnName = "description"
                targetColumnIndex = 1
            Case 1
                sourceColumnName = "description"
                targetColumnName = "code"
                targetColumnIndex = 0
            Case Else
                Return
        End Select

        Dim itemRow = itemBindingSource(itemBindingSource.Find(sourceColumnName, salesorder_dgv(sourceColumnIndex, rowIndex).Value))
        Dim code = CStr(itemRow(targetColumnName))

        salesorder_dgv(targetColumnIndex, rowIndex).Value = code
    End If
End Sub

您首先填充项目并将该数据绑定到组合框列,然后为销售额创建一个空 DataTable 并将其绑定到网格。该代码检查任何手动输入的代码是否确实与项目匹配,并且它将在手动输入代码时设置描述,并在从列表中选择描述时设置代码。它通过每次返回包含项目数据的 BindingSource 来做到这一点,因此没有额外的查询。您可能还想考虑检索每件商品的价格数据,并根据该数据和数量计算该行的价格。