winforms 对绑定数据源中的组合框项进行排序

yh2wf1be  于 2023-10-23  发布在  其他
关注(0)|答案(2)|浏览(115)

我一直在一遍又一遍地尝试让我的ComboBox项目正确排序,但我做的似乎都不起作用。我已经看了许多SO主题,并提出了各种建议,但无论我如何尝试,每次加载表单时,ComboBox中的项都不会排序。下面是我用来填充ComboBox的基本代码:

'ClaimCodes is a class-level variable
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
  • ComboBoxSelectedIndex属性更改为显示项目,而不是将其设置为-1(x)
  • 更改ComboBox属性设置的顺序,使DataSource设置在ValueMemberDisplayMember之前/之后
  • ComboBox.Sorted属性设置为true(* 当然,这会引发异常,因为对象绑定到DataSource *)

ClaimCodesDataTable(* 在应用任何排序之前 *)有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 *),并在Watch窗口中查看结果。从那里,它们显示为预期的排序(*1,10,11等 *)。但是,当显示表单时,ComboBox中的项目 * 未 * 排序,并以与原始未排序的DataTable相同的顺序显示(*10,11,13等 *)。
下面是我从SO问题中寻找灵感的几个例子:

我觉得我可能忽略了一些非常简单/愚蠢的事情,但我已经一遍又一遍地没有进展,也不知道为什么。我做错了什么?
还有一件事,我刚刚想到,这可能与此有关:这些ComboBox es具有以下属性:

*绘图模式:OwnerDrawFixed
*下拉样式:DropDownList
*FlatStyle:System
*已启用加密:True

不确定OwnerDrawFixed是否与此有关,但我使用它来实现一个自定义的.DrawItem事件处理程序,以便在选择ComboBox时突出显示它。

快速更新:

我将其中一个ComboBox es上的DrawMode切换回Normal并再次尝试,但项目仍然“未排序”。这似乎不是问题的原因。

附加代码:

如前所述,我正在为ComboBox es使用一个自定义的DrawItem处理程序,以便在选择它时进行视觉样式化。下面是该事件处理程序的代码,它基本上在每个ComboBox es上重用:

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
rbpvctlc

rbpvctlc1#

感谢@LarsTech和@Jimi的评论,帮助我找到了问题所在。当我在测试时,我也注意到选择并没有完全按照预期工作(* 我以前没有注意到这一点 ),所以他们的评论把我指向了一个完全不同的方向。这基本上是一个“粗略的草案”的解决方案,我扔在一起,让它工作。看看我的另一个被接受的答案,即代码的“清理”版本,它包含了来自评论的建议。
正如问题中提到的,我已经为这些ComboBox事件定制了一个DrawItem事件处理程序。该事件处理程序引用了“原始的”未排序的DataTable,以添加一些UX“美化”,以便用户能够更容易地识别何时选择ComboBox
如果没有它,用户很难看到何时框处于焦点 *)。
为了解决这个问题,我将ComboBox es使用的DataTable s移动到类级别,而不是在方法中:

Public Class MyForm
    Private ClaimCodes As DataTable
    Private ClaimCodeDescriptions As DataTable
    [...]
End Class

(* 是的,我重新使用了之前的ClaimCodes对象名称 *)
然后我修改了我的LoadCodeComboBoxes()方法来填充那些类级别的DataTable对象,而不是方法本地对象:

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

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

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

    ClaimCodeDescriptions = CodeTable.Copy
    ClaimCodeDescriptions.DefaultView.Sort = "descr ASC"
    ClaimCodeDescriptions = ClaimCodeDescriptions.DefaultView.ToTable

    CodeBinding.DataSource = ClaimCodes
    CodeBinding.Sort = "code"

    DescriptionBinding.DataSource = ClaimCodeDescriptions
    DescriptionBinding.Sort = "descr"

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

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

最后,我更新了我的DrawItem事件处理程序,以使用单个类级别的DataTable s:

Private Sub cboClaimCodeDescription_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles cboClaimCodeDescription.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.Blue
    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(ClaimCodeDescriptions(e.Index)("descr")) Then
            e.Graphics.DrawString(ClaimCodeDescriptions(e.Index)("descr").ToString, ComboBoxFont, TextBrush, New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
        End If
    End If
End Sub

现在每个都使用自己的排序DataTable,一切似乎都按预期工作。

aiqt4smr

aiqt4smr2#

“最终”修改代码,以从原始评论中删除建议

现在我能够找到意外行为的原因,我已经回去做了一些代码清理,以纳入@Jimi提出的建议。我已经完全删除了类级别的DataTable对象,并摆脱了LoadCodeComboBoxes()方法中的所有DefaultView“混乱”(* 注:**我已经完全限定了大多数对象类型,只是为了绝对清晰起见。您可以根据需要删除“无关的”名称空间。*):

Private Sub LoadCodeComboBoxes()
    Dim CodeTable As System.Data.DataTable
    Dim DescriptionBinding As BindingSource
    Dim CodeBinding As BindingSource

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

    CodeBinding = New BindingSource(CodeTable.Copy, Nothing) With {.Sort = "code ASC"}
    DescriptionBinding = New BindingSource(CodeTable.Copy, Nothing) With {.Sort = "descr ASC"}

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

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

此外,通过消除类级别的DataTable s并采纳使用事件参数属性的建议,我能够创建一个更“通用”的事件处理程序,可以将其分配给任何ComboBox

Public Sub DrawComboBoxItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs)
    If TypeOf (sender) Is System.Windows.Forms.ComboBox Then
        Dim ComboBox As System.Windows.Forms.ComboBox = DirectCast(sender, System.Windows.Forms.ComboBox)
        Dim ComboBoxColor As System.Drawing.Color
        Dim TextColor As System.Drawing.Color

        If (e.State And System.Windows.Forms.DrawItemState.Selected) = System.Windows.Forms.DrawItemState.Selected Then
            TextColor = System.Drawing.SystemColors.WindowText
            ComboBoxColor = System.Drawing.Color.Blue
        Else
            TextColor = System.Drawing.SystemColors.WindowText
            ComboBoxColor = System.Drawing.SystemColors.Window
        End If

        Using TextBrush As New System.Drawing.SolidBrush(TextColor)
            Using ComboBoxBrush As New System.Drawing.SolidBrush(ComboBoxColor)
                e.Graphics.FillRectangle(ComboBoxBrush, e.Bounds)

                If e.Index >= 0 Then
                    e.Graphics.DrawString(ComboBox.GetItemText(ComboBox.Items(e.Index)),
                                      e.Font,
                                      TextBrush,
                                      New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
                End If
            End Using
        End Using
    End If
End Sub
  • 这也合并了SolidBrush对象的Using语句,以便在方法中正确处理它们。*

有了这些,我删除了所有单独的表单内事件处理程序(cboClaimCode_DrawItem等),并将它们分配到一个应用其他事件处理程序的方法中:

Private Sub AddFormControlHandlers()
    AddHandler Me.cboClaimCode.DrawItem, AddressOf DrawComboBoxItem
    AddHandler Me.cboClaimCodeDescription.DrawItem, AddressOf DrawComboBoxItem
    [...]
End Sub

我希望这能帮助任何人寻找如何填充他们的ComboBox es。再次感谢@Jimi和@LarsTech的帮助和建议。

相关问题