You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

绑定数据源的ComboBox项排序失效问题求助

问题:绑定数据源的ComboBox无法正确排序

多次尝试让绑定数据源的ComboBox项正确排序,但始终无法成功。查阅多个相关话题并尝试多种方案后,每次加载窗体时ComboBox项仍未排序。以下是填充ComboBox的基础代码:

'ClaimCodes是类级变量
Private ClaimCodes As DataTable

Private Sub LoadCodeComboBoxes()
    Dim CodeTable As DataTable
    Dim DescriptionTable As DataTable
    Dim DescriptionBinding As New BindingSource
    Dim CodeBinding As New BindingSource

    Using MyDB As New DbConnection("my connection string")
        ClaimCodes = MyDB.ExecuteStatement("SELECT code, descr FROM code_table WHERE code > 0")
    End Using

    CodeTable = ClaimCodes.Copy
    CodeTable.DefaultView.Sort = "code ASC"
    CodeTable = CodeTable.DefaultView.ToTable

    DescriptionTable = ClaimCodes.Copy
    DescriptionTable.DefaultView.Sort = "descr ASC"
    DescriptionTable = DescriptionTable.DefaultView.ToTable

    CodeBinding.DataSource = CodeTable
    CodeBinding.Sort = "code"

    DescriptionBinding.DataSource = DescriptionTable
    DescriptionBinding.Sort = "descr"

    With Me.cboClaimCode
        .DataSource = Nothing
        .Items.Clear()
        .DataSource = CodeBinding
        .ValueMember = "code"
        .DisplayMember = "code"
        .AutoCompleteSource = AutoCompleteSource.ListItems
        .AutoCompleteMode = AutoCompleteMode.SuggestAppend
        .SelectedIndex = -1
    End With

    With Me.cboClaimCodeDescription
        .DataSource = Nothing
        .Items.Clear()
        .DataSource = DescriptionBinding
        .ValueMember = "code"
        .DisplayMember = "descr"
        .AutoCompleteSource = AutoCompleteSource.ListItems
        .AutoCompleteMode = AutoCompleteMode.SuggestAppend
        .SelectedIndex = -1
    End With
End Sub

尝试过的修改方案:

  • 完全移除BindingSource对象,直接使用XXXTable.DefaultView
  • 在DataTable层级、BindingSource层级或同时应用Sort
  • 修改ComboBox的SelectedIndex属性为显示某一项而非设为-1(未选中状态)
  • 调整ComboBox属性设置顺序,在ValueMember和DisplayMember之前/之后设置DataSource
  • 将ComboBox的.Sorted属性设为True(抛出异常,因为控件已绑定数据源)

未排序前的ClaimCodes DataTable包含17行数据:

+---------------------+
| code |    descr     |
+---------------------+
| 10   | <code desc>  |
| 11   | <code desc>  |
| 13   | <code desc>  |
| 14   | <code desc>  |
| 15   | <code desc>  |
| 30   | <code desc>  |
| 35   | <code desc>  |
| 99   | <code desc>  |
| 1    | <code desc>  |
| 36   | <code desc>  |
| 54   | <code desc>  |
| 60   | <code desc>  |
| 29   | <code desc>  |
| 61   | <code desc>  |
| 50   | <code desc>  |
| 71   | <code desc>  |
| 70   | <code desc>  |
+---------------------+

应用排序后,监视窗口显示DataTable和BindingSource已按预期排序(如1,10,11等),但窗体显示时ComboBox项仍保持原未排序DataTable的顺序(如10,11,13等)。

ComboBox相关属性:

  • DrawMode: OwnerDrawFixed
  • DropDownStyle: DropDownList
  • FlatStyle: System
  • FormattingEnabled: True

使用OwnerDrawFixed实现自定义.DrawItem事件处理程序以高亮选中项,改回Normal模式后问题依旧。自定义DrawItem代码:

Private Sub cboClaimCode_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles cboClaimCode.DrawItem
    Dim ComboBox As ComboBox = TryCast(sender, ComboBox)
    Dim ComboBoxFont As Font = ComboBox.Font
    Dim ComboBoxColor As Color
    Dim TextColor As Color
    Dim ComboBoxBounds As Rectangle = e.Bounds
    Dim TextBrush As SolidBrush
    Dim ComboBoxBrush As SolidBrush

    If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
        TextColor = SystemColors.WindowText
        ComboBoxColor = Colors.PaleBlue
    Else
        TextColor = SystemColors.WindowText
        ComboBoxColor = SystemColors.Window
    End If

    TextBrush = New SolidBrush(TextColor)
    ComboBoxBrush = New SolidBrush(ComboBoxColor)

    e.Graphics.FillRectangle(ComboBoxBrush, ComboBoxBounds)

    If e.Index >= 0 Then
        If Not IsCellEmpty(ClaimCodes(e.Index)("code")) Then
            e.Graphics.DrawString(ClaimCodes(e.Index)("code").ToString, ComboBoxFont, TextBrush, New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
        End If
    End If
End Sub

解决方案

问题出在自定义DrawItem事件处理程序中直接引用了未排序的类级DataTable ClaimCodes。

e.Index对应的是ComboBox绑定的已排序数据源(CodeBinding/CodeTable)的索引,但你在绘制文本时,却去取原表ClaimCodes的对应索引项,这就导致显示的内容还是原表的未排序顺序,和绑定的数据源排序完全无关。

修正后的DrawItem代码应该从ComboBox的绑定数据源中获取当前项,而不是直接用原表:

Private Sub cboClaimCode_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles cboClaimCode.DrawItem
    Dim ComboBox As ComboBox = TryCast(sender, ComboBox)
    Dim ComboBoxFont As Font = ComboBox.Font
    Dim ComboBoxColor As Color
    Dim TextColor As Color
    Dim ComboBoxBounds As Rectangle = e.Bounds
    Dim TextBrush As SolidBrush
    Dim ComboBoxBrush As SolidBrush

    If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
        TextColor = SystemColors.WindowText
        ComboBoxColor = Colors.PaleBlue
    Else
        TextColor = SystemColors.WindowText
        ComboBoxColor = SystemColors.Window
    End If

    TextBrush = New SolidBrush(TextColor)
    ComboBoxBrush = New SolidBrush(ComboBoxColor)

    e.Graphics.FillRectangle(ComboBoxBrush, ComboBoxBounds)

    If e.Index >= 0 Then
        ' 从绑定的数据源获取当前项,而不是原ClaimCodes表
        Dim currentRow As DataRowView = TryCast(ComboBox.Items(e.Index), DataRowView)
        If currentRow IsNot Nothing AndAlso Not IsCellEmpty(currentRow("code")) Then
            e.Graphics.DrawString(currentRow("code").ToString, ComboBoxFont, TextBrush, New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
        End If
    End If

    ' 释放资源避免内存泄漏
    TextBrush.Dispose()
    ComboBoxBrush.Dispose()
End Sub

同样的,cboClaimCodeDescription的DrawItem事件(如果有的话)也要做类似修改,从绑定的DescriptionBinding数据源中获取项,而不是原表。


内容的提问来源于stack exchange,提问作者G_Hosa_Phat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 19:34:52