VB.NET手动填充ComboBox后SelectedValue返回Nothing的问题求助
问题:ComboBox的SelectedValue返回Nothing,无法获取选中项的Value值
我需要将手动填充的ComboBox选项(数据为"C|Credit,D|Debit")中选中项的C或D值存入数据库。在Form_Load中调用以下代码填充ComboBox:
FillCombosWithValueMDisplayM(m01OpnCrDr, "C|Credit,D|Debit")
但调用CollectFormData函数收集窗体控件数据时,该ComboBox的SelectedValue返回Nothing。
填充ComboBox的公共子过程
Sub FillCombosWithValueMDisplayM(CtrlCombo As ComboBox, CommaSepVDPair As String) Dim pairs() = CommaSepVDPair.Split(",") For Each pair As String In pairs Dim KeyVal() = pair.Split("|") Dim newItem As New With {.Value = KeyVal(0).ToString, .Display = KeyVal(1).ToString} CtrlCombo.Items.Add(newItem) Next CtrlCombo.ValueMember = "Value" CtrlCombo.DisplayMember = "Display" End Sub
收集窗体数据的公共函数(部分代码)
Function CollectFormData(frm As Form, tbl As DataTable) 'Function CollectFormData(frm As Form, FieldList As List(Of String)) As Dictionary(Of String, Object) Dim FormDataDict As New Dictionary(Of String, Object) Dim CtlType As Type Dim CtrlArray As Control() Dim Ctrl As Control Dim fldname As String Dim FldType As OleDb.OleDbType Dim CtlValuePlaceHolder As Object For Each row As DataRow In tbl.Rows fldname = row("COLUMN_NAME") FldType = row("DATA_TYPE").ToString If fldname = "ID" Then 'do nothing Else CtrlArray = frm.Controls.Find(fldname, True) If CtrlArray.Count > 0 Then Ctrl = CtrlArray(0) CtlType = Ctrl.GetType Select Case CtlType Case GetType(TextBox) CtlValuePlaceHolder = CNulls(TryCast(Ctrl, TextBox).Text, FldType) FormDataDict.Add(fldname, CtlValuePlaceHolder) Case GetType(ComboBox) '(here combobox m01OpnCrDr returns Nothing) 'CtlValuePlaceHolder = CNulls(TryCast(Ctrl, ComboBox).SelectedValue, FldType) Dim selectedValue As Object = TryCast(Ctrl, ComboBox).SelectedValue CtlValuePlaceHolder = CNulls(selectedValue, FldType) FormDataDict.Add(fldname, CtlValuePlaceHolder) Case GetType(C1.Win.Calendar.C1DateEdit) CtlValuePlaceHolder = CNulls(TryCast(Ctrl, C1.Win.Calendar.C1DateEdit).Value, FldType) FormDataDict.Add(fldname, CtlValuePlaceHolder) Case GetType(CheckBox) If TryCast(Ctrl, CheckBox).Checked Then CtlValuePlaceHolder = True Else CtlValuePlaceHolder = False End If FormDataDict.Add(fldname, CtlValuePlaceHolder) Case GetType(DateTimePicker) CtlValuePlaceHolder = CNulls(TryCast(Ctrl, DateTimePicker).Value, FldType) FormDataDict.Add(fldname, CtlValuePlaceHolder) Case GetType(C1.Win.Calendar.C1DateEdit) CtlValuePlaceHolder = CNulls(TryCast(Ctrl, C1.Win.Calendar.C1DateEdit).Value, FldType) FormDataDict.Add(fldname, CtlValuePlaceHolder) Case GetType(MaskedTextBox) CtlValuePlaceHolder = CNulls(TryCast(Ctrl, MaskedTextBox).Text, FldType) FormDataDict.Add(fldname, CtlValuePlaceHolder) End Select End If End If Next Return FormDataDict End Function
问题原因
填充ComboBox时使用了匿名类型(New With {.Value = ..., .Display = ...}),WinForms的ComboBox无法正确识别匿名类型的属性来绑定ValueMember,导致SelectedValue始终返回Nothing。匿名类型是编译器内部生成的私有类型,ComboBox的绑定机制无法正常解析其属性。
解决方案
方法一:使用强类型类替代匿名类型(推荐)
定义一个公开的类来存储ComboBox的选项数据:
Public Class ComboItem Public Property Value As String Public Property Display As String End Class
修改填充ComboBox的子过程,使用该类创建选项:
Sub FillCombosWithValueMDisplayM(CtrlCombo As ComboBox, CommaSepVDPair As String) Dim pairs() = CommaSepVDPair.Split(",") For Each pair As String In pairs Dim KeyVal() = pair.Split("|") Dim newItem As New ComboItem() With { .Value = KeyVal(0).ToString, .Display = KeyVal(1).ToString } CtrlCombo.Items.Add(newItem) Next CtrlCombo.ValueMember = "Value" CtrlCombo.DisplayMember = "Display" End Sub
这样ComboBox可以正确识别ValueMember和DisplayMember,SelectedValue就能正常返回选中项的Value值。
方法二:通过SelectedItem反射获取属性值
如果不想修改填充逻辑,可以在CollectFormData函数中,通过反射从SelectedItem中提取Value属性:
Case GetType(ComboBox) Dim cbo = TryCast(Ctrl, ComboBox) Dim selectedValue As Object = Nothing If cbo.SelectedItem IsNot Nothing Then ' 反射获取匿名类型的Value属性值 selectedValue = cbo.SelectedItem.GetType().GetProperty("Value").GetValue(cbo.SelectedItem, Nothing) End If CtlValuePlaceHolder = CNulls(selectedValue, FldType) FormDataDict.Add(fldname, CtlValuePlaceHolder)
这种方法不需要修改填充逻辑,但反射会带来一定的性能开销,且代码维护性不如强类型方案。
内容的提问来源于stack exchange,提问作者Hemu
相关产品推荐
相关产品推荐

