如何为绑定枚举的ComboBox设置SelectedValue

3

我已经将一个ComboBox绑定到了一个枚举类型,代码如下:

Enum Foo
    Bar
    Baz
End Enum

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    FooComboBox.DataSource = [Enum].GetValues(GetType(Foo))
End Sub

我想在特定时间更改所选的值

Private Sub FooBar()
    FooComboBox.SelectedValue = Foo.Bar
End Sub

但是这会引发一个异常:
未处理的类型为“System.InvalidOperationException”的异常在System.Windows.Forms.dll中发生
附加信息:无法在带有空ValueMember的ListControl中设置SelectedValue。
检查ComboBox后,我确实看到ValueMember属性为空。但是当我读取它时,SelectedValue确实等于当前选定项所期望的值,这表明ValueMember已设置为预期的枚举值。
A)如果ValueMember属性未设置,则SelectedValue中的值来自哪里?
B)如果绑定使SelectedValue包含正确的值,则调整为什么不起作用?
C)我需要做什么才能使.Net正常工作?
请注意,确实有很多关于C#和WPF的问题,但我还没有看到任何一个真正解释这一点,并且我无法从建议的答案中找出正确的VB语法。

我可以使用 SelectedIndex 来实现所需的效果,但是这是因为巧合的是项目的索引与枚举值匹配。当枚举值与索引不同时会发生什么? - Toby
FooComboBox.SelectedItem = Foo.Bar 当它们不是简单的{0,1,2}值时,使用类型设置Value和Displaymember。 - Ňɏssa Pøngjǣrdenlarp
@Plutonix,如何“使用类型来设置值和显示成员”? - Toby
有一个关于在ComboBox中赋值的问题,使用Enum.Names和Enum.Values; 还有一个关于如何在ComboBox上设置DisplayMember和ValueMember而不使用DataSource的问题,这让问题变得比它需要的更复杂。 - Ňɏssa Pøngjǣrdenlarp
听起来不错。使用反射获取名称?也许您可以添加一个简短的示例作为答案? - Toby
显示剩余2条评论
1个回答

3
特别是当值不连续时,您需要为控件提供一种将名称映射到相关值的方法。一旦您将Enum.GetValues或名称发布到CBO,它们就变得分离了。
您可以使用类似于KeyValuesPair(of String, Int32)的内容,其中名称用作TKey,而值用作TValue。泛型使其看起来比实际复杂。由于键始终为String,而值通常为Int32,因此我倾向于为此使用简单的NameValuePair类:
Public Class NameValuePair
    Public Property Name As String
    Public Property Value As Int32

    Public Sub New(n As String, v As Int32)
        Name = n
        Value = v
    End Sub

    Public Overrides Function ToString() As String
        Return String.Format("{0}", Name)
    End Function
End Class

这将把任何名称与任何值关联起来。最重要的是,您可以控制ToString()的显示内容。在这种情况下,名称和值都来自一个枚举; 一种创建列表或数组的简单方法:

Private Enum Stooges
    Moe = 9
    Larry = 99
    Curly = 45
    Shemp = 65
    CurlyJoe = 8
End Enum

' method to convert any Enum to a collection of Named-Value pairs
Private Function EnumToPairsList(e As Type) As List(Of NameValuePair)
    Dim ret As New List(Of NameValuePair)

    Dim vals = [Enum].GetValues(e)
    Dim names = [Enum].GetNames(e).ToArray

    For n As Int32 = 0 To names.Count - 1
        ret.Add(New NameValuePair(names(n), CType(vals.GetValue(n), Int32)))
    Next

    Return ret
End Function

EnumsToPairsList 可以返回一个数组,或根据需要使用 KeyValuePair。当存在 Description 时,它可以扩展为使用描述替代名称。使用方法如下:

cbox1.DataSource = EnumToPairsList(GetType(Stooges))
cbox1.DisplayMember = "Name"   ' use "Key" for a KVP
cbox1.ValueMember = "Value"

' set a value:
cbox1.SelectedValue = Convert.ToInt32(Stooges.Shemp)

使用方法

由于您已将枚举包装在NVP类中,因此每个SelectedItem都会被包含在一个Object中。使用这样的DataSource时,通常会在SelectedValueChanged事件中进行操作并检查SelectedValue。这是它的主要目的:向用户显示名称,但在代码中返回枚举值。

唯一的“技巧”是需要将其强制转换回您的枚举:

Private Sub cbox1_SelectedValueChanged(...
    Dim eItem As Stooges = CType(cbox1.SelectedValue, Stooges)
    Console.WriteLine(eItem)
    Console.WriteLine(eItem.ToString)        

45
卷曲的

如果您坚持使用 SelectedItem,您将不得不从 Object 转换为 NameValuePair,获得 Value,然后将其转换为枚举类型。


通常有用的是保留数据源的副本,以便它不仅作为控件数据源而且还存在于其他地方:

Private StoogesDS As List(Of NameValuePair)
...
StoogesDS = EnumsToPairsList(GetType(Stooges))
cbox1.DataSource = StoogesDS

这使得即使表单不存在,您的代码仍然可以使用该集合。如上所述,现在每个项目都是一个NameValuePair对象。
cbox1.SelectedItem = StoogesDS.FirstOrDefault(Function(z) z.Name = Stooges.Shemp.ToString())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接