如何在 VB 中填充主要细节组合框?

How to populate master detail combobox in VB?

我有两个 Comboboxes 具有 Master Detail 关系 Table BankBranch

我的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,然后设置 DisplayMemberValueMember。这是错误的,也是您出现问题的原因。当您设置 DataSource 时,您就完成了绑定,所以一切都会发生。列表中的第一项被选中,因此您的 SelectedIndexChanged 处理程序被执行。您尚未设置 DisplayMemberValueMember,因此 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 表,就像我在示例中所做的那样。