如何制作触发循环的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
来做到这一点,因此没有额外的查询。您可能还想考虑检索每件商品的价格数据,并根据该数据和数量计算该行的价格。
我正在开发销售订单应用程序。我正在使用数据网格视图来填写销售订单。
我的数据网格视图中的字段如下所示
物品代码 - 物品描述 - 数量 - 价格
描述字段是一个组合框。
我想要的是当用户输入 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
来做到这一点,因此没有额外的查询。您可能还想考虑检索每件商品的价格数据,并根据该数据和数量计算该行的价格。