绑定数据源的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
相关产品推荐
相关产品推荐

