【问题标题】:letting user type and add value to an already bound combobox让用户键入并为已绑定的组合框添加值
【发布时间】:2026-02-05 08:20:10
【问题描述】:

我的应用程序中有一个组合框,其中填充了来自 SQL SERVER 的十几个项目。我目前正在尝试让用户能够在组合框中输入新值并将其保存到 SQL SERVER 中的表中。我正在寻找类似于 DropDown 和 DropDownList 的 DropDownStyle 的东西。基本上我希望让用户在组合框中输入,但是如果值不存在,我希望能够给他们一个保存它的选项(可能是失去焦点)。我想知道做这种事情的好方法是什么。

在失去焦点时,我应该基本上浏览当前下拉列表中的每个项目并根据输入的值检查它吗?

编辑:

    Sql="SELECT IDvalue, TextName from TblReference"
    Dim objconn As New SqlConnection
    objconn = New SqlConnection(conn)
    objconn.Open()

    Dim da As New SqlDataAdapter(sql, objconn)
    Dim ds As New DataSet

    da.Fill(ds, "Name")

    If ds.Tables("Name").Rows.Count > 0 Then
    Dim dr As DataRow = ds.Tables("Name ").NewRow
    ds.Tables("Name").Rows.InsertAt(dr, 0)
    With c
         .DataSource = ds.Tables("Name")
         .ValueMember = " IDvalue "
         .DisplayMember = " TextName "
    End With
    End If

【问题讨论】:

  • 请显示将项目添加到您的组合的代码。您是使用 DataSource 属性还是使用 Add 一项一项地添加项目?
  • @steve - 见编辑! C- 是传递给此函数的组合框名称
  • 嗯,我认为您需要像 msdn.microsoft.com/en-us/library/office/ff845736.aspx 这样的东西,但据我所知,标准组合框没有内置功能。似乎唯一可能的方法是处理 Leave 事件并检查 ds.Tables("Names") 行中是否存在 ComboBox.Text
  • 我一直这样做的方式是添加一个小的编辑按钮。当他们单击它时,会出现一个对话框,如果代码批准,它将添加到数据源中。 PS你的代码泄露了
  • @PLUTONIX 这就是我想做的,但上帝说不。在哪里?!它似乎工作正常

标签: vb.net combobox


【解决方案1】:

您已经在表格中添加了假/空白行,您可以为新项目做同样的事情。

' form level datatable var
Private cboeDT As DataTable

正在初始化:

cboeDT = New DataTable

Dim sql = "SELECT Id, Descr FROM TABLENAME ORDER BY Descr"
Using dbcon As New MySqlConnection(MySQLConnStr)
    Using cmd As New MySqlCommand(sql, dbcon)
        dbcon.Open()

        cboeDT.Load(cmd.ExecuteReader())

        ' probably always need this even
        ' when there are no table rows (???)
        Dim dr = cboeDT.NewRow
        dr("Id") = -1       ' need a way to identify it
        dr("Descr") = ""
        cboeDT.Rows.InsertAt(dr, 0)
    End Using
End Using

cboeDT.DefaultView.Sort = "Descr ASC"

cboE.DataSource = cboeDT
cboE.DisplayMember = "Descr"
cboE.ValueMember = "Id"

注意 用户往往对这些事物的顺序有偏好。简单的生物往往更喜欢按字母顺序排列的数据库 ID,而不是他们可能永远不会看到的数据库 ID。为了适应它们,DefaultView 进行了排序,以便添加的任何新行都将以正确的顺序显示。

在离开事件中添加新项目(很像史蒂夫的):

Private Sub cboE_Leave(sender ...
    ' if it is new, there will be no value
    If cboE.SelectedValue Is Nothing Then
        ' alternatively, search for the text:
        'Dim item = cboeDT.AsEnumerable().
        '    FirstOrDefault(Function(q) String.Compare(q.Field(Of String)("Descr"),
        '                                                            cboE.Text, True) = 0)
        'If item Is Nothing Then 
        '    ' its new...

        Dim newid = AddNewItem(cboE.Text)

        Dim dr = cboeDT.NewRow
        dr("Id") = newid
        dr("Descr") = cboE.Text
        cboeDT.Rows.Add(dr)

        ' fiddling with the DS looses the selection,
        '    put it back
        cboE.SelectedValue = newid
    End If
End Sub

如果要按文本搜索:

Dim item = cboeDT.AsEnumerable().
    FirstOrDefault(Function(q) String.Compare(q.Field(Of String)("Descr"),
                                                            cboE.Text, True) = 0)
If item Is Nothing Then
    ' its new...
    ...

根据实际的数据库,插入会有所不同。一个关键步骤是捕获并返回新项目的 ID,因为 CBO 需要它:

Private Function AddNewItem(newItem As String) As Int32

    Dim sql = "INSERT INTO MY_TABLE (Descr) VALUES (@v); SELECT LAST_INSERT_ID();"
    Dim newId = -1
    Using dbcon As New MySqlConnection(MySQLConnStr)
        Using cmd As New MySqlCommand(sql, dbcon)
            dbcon.Open()

            cmd.Parameters.Add("@v", MySqlDbType.String).Value = newItem

            ' MySql provides it in the command object
            If cmd.ExecuteNonQuery() = 1 Then
                newId = Convert.ToInt32(cmd.LastInsertedId)
            End If
        End Using
    End Using

    Return newId
End Function

如前所述,MySql 提供了LastInsertedID 作为命令对象属性。在 SQL SERVER 中,将 ...";SELECT LAST_INSERT_ID();" 添加到 SQL 的末尾,然后:

newId = Convert.ToInt32(cmd.ExecuteScalar())

这在概念上与史蒂夫的答案没有太大不同,只是它使用您构建的 DataTable 而不是集合,这使得它(非常)稍微简单一些。

【讨论】:

  • 谢谢你。终于可以试一试了。我需要将 DropDownStyle 更改为 DropDown 以便我可以键入文本并且它不会将其限制为仅组合框中的内容。但是,如果我开始在下拉菜单中输入项目的名称 - 它只会在我按下向下箭头键时出现。有没有办法像点击它一样自动打开下拉菜单并显示输入文本的所有项目。或者只是一种自动查找正在输入的项目的不同方式?
  • 好的,所以我可以通过*.com/questions/18670245/… 找到这个问题,所以这项工作是因为它会自动打开下拉菜单,但是,这是另一个问题。当用户点击下拉菜单时,我使用一个函数来调整下拉菜单的宽度。在这种情况下,建议仅以相同的宽度和下拉列表显示。啊啊啊。 DropDownwidth 不适用于那个
【解决方案2】:

我这样做的方式是在窗口加载时执行新的 SQL 查询以获取表中的值列表,并将它们加载到组合框中。

然后一旦失去焦点,它就会检查当前输入到组合框中的内容与当前已加载的值。如果它不存在,那么它不在 SQL 中。类似于以下内容...

    Dim found As Boolean = False

    For i As Integer = 0 To comboBox.Items.Count - 1
        Dim value As String = comboBox.Items(i).ToString()
        If comboBox.Text = value Then
            found = True
        End If
    Next

    If found = False Then
        'the item doesn't exist.. add it to SQL
    Else
        'the item exists.. no need to touch SQL
    End If

【讨论】:

  • 但是如果第一次没有找到它,它只会循环一次。因为如果它不满足第一个和第二个条件,我不希望它进入 sql,直到它遍历每个单个值
  • 我有点困惑,它会遍历每个值一次。如果它找到该值,它不会尝试添加它,因为它已经存在并停止循环。如果它没有找到该值,则在遍历每个值之后,它将添加它。如果找到项目就没有理由继续循环?
  • 已编辑的答案,这可能就是您要找的。​​span>
  • 我就像他在说什么......拍了拍我的头然后意识到它大声笑。如果它回答了您的问题,请不要忘记标记为答案:)
【解决方案3】:

我要做的第一件事是构建一个简单的类来通过这个类的列表来保存你的值

Public Class DataItem
   Public Property IDValue As Integer
   Public Property TextName as String
End Class

现在,不用构建 SqlDataAdapter 并填充数据集,而是使用 SqlDataReader 并构建 List(Of DataItem)

' Class global...
Dim allItems = new List(Of DataItem)()

Sql="SELECT IDvalue, TextName from TblReference"

' Using to avoid leaks on disposable objects
Using objconn As New SqlConnection(conn)
Using cmd As New SqlCommand(Sql, objconn)
    objconn.Open()
    Using reader = cmd.ExecuteReader()
       While reader.Read()
          Dim item = new DataItem() With { .IDValue = reader.GetInt32(0), .TextName = reader.GetString(1)}
          allItems.Add(item)
       End While
    End Using
    if allItems.Count > 0 Then
       allItems.Insert(0, new DataItem() With {.IDValue = -1, .TextValue = ""}
       Dim bs = new BindingList(Of DataItem)(allItems)
       c.DataSource = bs 
       c.ValueMember = "IDvalue"
       c.DisplayMember = "TextName"
    End If
End Using
End Using

现在是您要添加到组合框的离开事件的代码

Sub c_Leave(sender As Object, e As EventArgs) Handles c.Leave
    If Not String.IsNullOrEmpty(c.Text) Then
        Dim bs = DirectCast(c.DataSource, BindingList(Of DataItem))
        if bs.FirstOrDefault(Function(x) x.TextName = c.Text) Is Nothing Then
            Dim item = new DataItem() With { .IDValue = -1, .TextName = c.Text}
            bs.Add(item)

            ' here add the code to insert in the database
        End If
    End If
End Sub

【讨论】: