如何在 VB 中填充主要细节组合框?
How to populate master detail combobox in VB?
我有两个 Comboboxes 具有 Master Detail 关系 Table Bank 和 Branch
我的VB代码隐藏:-
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using cmd As New OleDbCommand("Select Bank, ID from Bank", con)
Dim da As New OleDbDataAdapter(cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox1.DataSource = dt
ComboBox1.DisplayMember = "Bank"
ComboBox1.ValueMember = "ID"
ComboBox1.Text = "Select"
End Using
End Using
End Sub
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
Dim bankId = ComboBox1.SelectedValue.ToString
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using branch_cmd As New OleDbCommand("Select Branch from Branch where Bank_id ='" & bankId & "'", con)
Dim da As New OleDbDataAdapter(branch_cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox2.DataSource = dt
ComboBox2.DisplayMember = "Bank"
ComboBox2.ValueMember = "ID"
End Using
End Using
End Sub
End Class
我想根据第一个组合框选择的值填充第二个组合框,但代码出错
ComboBox1_SelectedIndexChanged 函数:
并且,通过调试 branch_cmd sql 是:
Select Branch from Branch where Bank_id ='System.Data.DataRowView'
就目前情况而言,我可以看到两个错误。首先,不要连接字符串来构建 sql 命令文本。始终使用参数化查询,准确指定您传递的参数的数据类型。第二个错误出现在第二个组合的 DisplayMember 和 ValueMember 中。你这里没有 Bank_Id 或银行名称,但有分行名称
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
' Always check if you have a valid selection to avoid NRE.
if ComboBox1.SelectedValue Is Nothing Then
Return
End if
' If bankid is an integer then convert to an integer
Dim bankId as Integer = Convert.ToInt32(ComboBox1.SelectedValue)
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using branch_cmd As New OleDbCommand("Select Branch from Branch where Bank_id =@id", con)
cmd.Parameters.Add("@id", OleDbType.Integer).Value = bankId
Dim da As New OleDbDataAdapter(branch_cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox2.DataSource = dt
ComboBox2.DisplayMember = "Branch"
ComboBox2.ValueMember = "Branch"
End Using
End Using
End Sub
根据您在下面的评论,您得到 System.Data.DataRowView 作为包含在 SelectedValue 中的元素。这不应该发生在您的问题中显示的代码中,所以也许是某些不同的东西导致了问题。 (例如,如果数据表字段名称与 ValueMember/DisplayMember 属性不匹配)
无论如何,从 DataRowView 中,您应该能够以这种方式获取整数
Dim drv = DirectCast(ComboBox1.SelectedValue, DataRowView)
if drv IsNot Nothing then
Dim bankid = Convert.ToInt32(drv("ID"))
...
End if
编辑:
我正要添加一个注释,然后我意识到这个注释实际上是您实际问题的解决方案。您首先设置 DataSource
,然后设置 DisplayMember
和 ValueMember
。这是错误的,也是您出现问题的原因。当您设置 DataSource
时,您就完成了绑定,所以一切都会发生。列表中的第一项被选中,因此您的 SelectedIndexChanged
处理程序被执行。您尚未设置 DisplayMember
或 ValueMember
,因此 SelectedValue
不会 return 适当的值。总是最后设置 DataSource
,正如我在下面的示例中所做的那样。
原文:
除非你有大量数据,否则你应该直接从两个表中获取所有数据,绑定数据,然后让绑定自动进行过滤。您可以使用 BindingSources
并将 child 绑定到 DataRelation
而不是 DataTable
。看哪!
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim parentTable = GetParentTable()
Dim childTable = GetChildTable()
Dim data As New DataSet
'Create a foreign key relation between the tables.
data.Tables.Add(parentTable)
data.Tables.Add(childTable)
data.Relations.Add("ParentChild", parentTable.Columns("ParentId"), childTable.Columns("ParentId"))
'Bind the parent BindingSource to the parent table.
parentBindingSource.DataMember = "Parent"
parentBindingSource.DataSource = data
'Bind the child BindingSource to the relation.
childBindingSource.DataMember = "ParentChild"
childBindingSource.DataSource = parentBindingSource
parentComboBox.DisplayMember = "ParentName"
parentComboBox.ValueMember = "ParentId"
parentComboBox.DataSource = parentBindingSource
childComboBox.DisplayMember = "ChildName"
childComboBox.ValueMember = "ChildId"
childComboBox.DataSource = childBindingSource
End Sub
Private Function GetParentTable() As DataTable
Dim table As New DataTable("Parent")
table.PrimaryKey = {table.Columns.Add("ParentId", GetType(Integer))}
table.Columns.Add("ParentName", GetType(String))
table.Rows.Add(1, "Parent 1")
table.Rows.Add(2, "Parent 2")
table.Rows.Add(3, "Parent 3")
Return table
End Function
Private Function GetChildTable() As DataTable
Dim table As New DataTable("Child")
table.PrimaryKey = {table.Columns.Add("ChildId", GetType(Integer))}
table.Columns.Add("ChildName", GetType(String))
table.Columns.Add("ParentId", GetType(Integer))
table.Rows.Add(1, "Child 1.1", 1)
table.Rows.Add(2, "Child 1.2", 1)
table.Rows.Add(3, "Child 1.3", 1)
table.Rows.Add(4, "Child 2.1", 2)
table.Rows.Add(5, "Child 2.2", 2)
table.Rows.Add(6, "Child 2.3", 2)
table.Rows.Add(7, "Child 3.1", 3)
table.Rows.Add(8, "Child 3.2", 3)
table.Rows.Add(9, "Child 3.3", 3)
Return table
End Function
如果您这样做,选择 parent 将自动过滤显示的 children 以供选择。
如果不是很明显,您可以通过查询数据库而不是手动构建它们来获得 parent 和 child 表,就像我在示例中所做的那样。
我有两个 Comboboxes 具有 Master Detail 关系 Table Bank 和 Branch
我的VB代码隐藏:-
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using cmd As New OleDbCommand("Select Bank, ID from Bank", con)
Dim da As New OleDbDataAdapter(cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox1.DataSource = dt
ComboBox1.DisplayMember = "Bank"
ComboBox1.ValueMember = "ID"
ComboBox1.Text = "Select"
End Using
End Using
End Sub
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
Dim bankId = ComboBox1.SelectedValue.ToString
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using branch_cmd As New OleDbCommand("Select Branch from Branch where Bank_id ='" & bankId & "'", con)
Dim da As New OleDbDataAdapter(branch_cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox2.DataSource = dt
ComboBox2.DisplayMember = "Bank"
ComboBox2.ValueMember = "ID"
End Using
End Using
End Sub
End Class
我想根据第一个组合框选择的值填充第二个组合框,但代码出错 ComboBox1_SelectedIndexChanged 函数:
并且,通过调试 branch_cmd sql 是:
Select Branch from Branch where Bank_id ='System.Data.DataRowView'
就目前情况而言,我可以看到两个错误。首先,不要连接字符串来构建 sql 命令文本。始终使用参数化查询,准确指定您传递的参数的数据类型。第二个错误出现在第二个组合的 DisplayMember 和 ValueMember 中。你这里没有 Bank_Id 或银行名称,但有分行名称
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
' Always check if you have a valid selection to avoid NRE.
if ComboBox1.SelectedValue Is Nothing Then
Return
End if
' If bankid is an integer then convert to an integer
Dim bankId as Integer = Convert.ToInt32(ComboBox1.SelectedValue)
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using branch_cmd As New OleDbCommand("Select Branch from Branch where Bank_id =@id", con)
cmd.Parameters.Add("@id", OleDbType.Integer).Value = bankId
Dim da As New OleDbDataAdapter(branch_cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox2.DataSource = dt
ComboBox2.DisplayMember = "Branch"
ComboBox2.ValueMember = "Branch"
End Using
End Using
End Sub
根据您在下面的评论,您得到 System.Data.DataRowView 作为包含在 SelectedValue 中的元素。这不应该发生在您的问题中显示的代码中,所以也许是某些不同的东西导致了问题。 (例如,如果数据表字段名称与 ValueMember/DisplayMember 属性不匹配)
无论如何,从 DataRowView 中,您应该能够以这种方式获取整数
Dim drv = DirectCast(ComboBox1.SelectedValue, DataRowView)
if drv IsNot Nothing then
Dim bankid = Convert.ToInt32(drv("ID"))
...
End if
编辑:
我正要添加一个注释,然后我意识到这个注释实际上是您实际问题的解决方案。您首先设置 DataSource
,然后设置 DisplayMember
和 ValueMember
。这是错误的,也是您出现问题的原因。当您设置 DataSource
时,您就完成了绑定,所以一切都会发生。列表中的第一项被选中,因此您的 SelectedIndexChanged
处理程序被执行。您尚未设置 DisplayMember
或 ValueMember
,因此 SelectedValue
不会 return 适当的值。总是最后设置 DataSource
,正如我在下面的示例中所做的那样。
原文:
除非你有大量数据,否则你应该直接从两个表中获取所有数据,绑定数据,然后让绑定自动进行过滤。您可以使用 BindingSources
并将 child 绑定到 DataRelation
而不是 DataTable
。看哪!
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim parentTable = GetParentTable()
Dim childTable = GetChildTable()
Dim data As New DataSet
'Create a foreign key relation between the tables.
data.Tables.Add(parentTable)
data.Tables.Add(childTable)
data.Relations.Add("ParentChild", parentTable.Columns("ParentId"), childTable.Columns("ParentId"))
'Bind the parent BindingSource to the parent table.
parentBindingSource.DataMember = "Parent"
parentBindingSource.DataSource = data
'Bind the child BindingSource to the relation.
childBindingSource.DataMember = "ParentChild"
childBindingSource.DataSource = parentBindingSource
parentComboBox.DisplayMember = "ParentName"
parentComboBox.ValueMember = "ParentId"
parentComboBox.DataSource = parentBindingSource
childComboBox.DisplayMember = "ChildName"
childComboBox.ValueMember = "ChildId"
childComboBox.DataSource = childBindingSource
End Sub
Private Function GetParentTable() As DataTable
Dim table As New DataTable("Parent")
table.PrimaryKey = {table.Columns.Add("ParentId", GetType(Integer))}
table.Columns.Add("ParentName", GetType(String))
table.Rows.Add(1, "Parent 1")
table.Rows.Add(2, "Parent 2")
table.Rows.Add(3, "Parent 3")
Return table
End Function
Private Function GetChildTable() As DataTable
Dim table As New DataTable("Child")
table.PrimaryKey = {table.Columns.Add("ChildId", GetType(Integer))}
table.Columns.Add("ChildName", GetType(String))
table.Columns.Add("ParentId", GetType(Integer))
table.Rows.Add(1, "Child 1.1", 1)
table.Rows.Add(2, "Child 1.2", 1)
table.Rows.Add(3, "Child 1.3", 1)
table.Rows.Add(4, "Child 2.1", 2)
table.Rows.Add(5, "Child 2.2", 2)
table.Rows.Add(6, "Child 2.3", 2)
table.Rows.Add(7, "Child 3.1", 3)
table.Rows.Add(8, "Child 3.2", 3)
table.Rows.Add(9, "Child 3.3", 3)
Return table
End Function
如果您这样做,选择 parent 将自动过滤显示的 children 以供选择。
如果不是很明显,您可以通过查询数据库而不是手动构建它们来获得 parent 和 child 表,就像我在示例中所做的那样。