将枚举的所有描述绑定到组合框

时间:2021-02-12 14:35:06

标签: vb.net enums combobox

如何将枚举的格式化字符串描述绑定到组合框?我希望组合框显示说明,而不是枚举。

我有一个这样的枚举:

Public Enum Sandwiches
<ComponentModel.Description("Ham Sandwich")> HamSandwich
<ComponentModel.Description("Reuben")> Reuben
<ComponentModel.Description("Po’ Boy")> PoBoy
<ComponentModel.Description("Grilled Cheese")> GrilledCheese
End Enum

在描述字符串已绑定到组合框后,我可以使用此函数将所选项目转换回枚举:

        Public Function GetEnumFromDescriptionAttribute(Of T)(description As String) As T
        Dim type As Type = GetType(T)
        If Not type.IsEnum Then
            Throw New InvalidOperationException()
        End If
        For Each fi As Reflection.FieldInfo In type.GetFields()
            Dim descriptionAttribute As ComponentModel.DescriptionAttribute = TryCast(Attribute.GetCustomAttribute(fi, GetType(ComponentModel.DescriptionAttribute)), ComponentModel.DescriptionAttribute)
            If descriptionAttribute IsNot Nothing Then
                If descriptionAttribute.Description <> description Then
                    Continue For
                End If
                Return DirectCast(fi.GetValue(Nothing), T)
            End If
            If fi.Name <> description Then
                Continue For
            End If
            Return DirectCast(fi.GetValue(Nothing), T)
        Next
        Return Nothing
    End Function

但我找不到一种干净的方法将所有枚举绑定到组合框。我知道一个经常建议的解决方案是将枚举和枚举描述转换为字典,然后将它们设置为 cbo DisplayMember 和 ValueMember。但我不知道如何真正做到这一点。

我已经准备好了几十个关于如何做到这一点的部分解决方案;问题是它们都是用 C# 编写的,在 Option Strict 关闭的情况下使用隐式转换,并且没有显示完整的实现。因此,我无法将任何解决方案转换为 .NET,因为我不知道已定义的类型变量是什么。

2 个答案:

答案 0 :(得分:1)

这是我前段时间写的一个类,用于表示一个 Enum 值及其描述:

Imports System.ComponentModel
Imports System.Reflection
 
''' <summary>
''' Contains an enumerated constant value and a friendly description of that value, if one exists.
''' </summary>
''' <typeparam name="T">
''' The enumerated type of the value.
''' </typeparam>
Public Class EnumDescriptor(Of T)
 
    ''' <summary>
    ''' The friendly description of the value.
    ''' </summary>
    Private _description As String
    ''' <summary>
    ''' The enumerated constant value.
    ''' </summary>
    Private _value As T
 
    ''' <summary>
    ''' Gets the friendly description of the value.
    ''' </summary>
    ''' <value>
    ''' A <b>String</b> containing the value's Description attribute value if one exists; otherwise, the value name.
    ''' </value>
    Public ReadOnly Property Description() As String
        Get
            Return Me._description
        End Get
    End Property
 
    ''' <summary>
    ''' Gets the enumerated constant value.
    ''' </summary>
    ''' <value>
    ''' An enumerated constant of the <b>EnumDescriptor's</b> generic parameter type.
    ''' </value>
    Public ReadOnly Property Value() As T
        Get
            Return Me._value
        End Get
    End Property
 
    ''' <summary>
    ''' Creates a new instance of the <b>EnumDescriptor</b> class.
    ''' </summary>
    ''' <param name="value">
    ''' The value to provide a description for.
    ''' </param>
    Public Sub New(ByVal value As T)
        Me._value = value
 
        'Get the Description attribute.
        Dim field As FieldInfo = value.GetType().GetField(value.ToString())
        Dim attributes As DescriptionAttribute() = DirectCast(field.GetCustomAttributes(GetType(DescriptionAttribute), _
                                                                                        False),  _
                                                              DescriptionAttribute())
 
        'Use the Description attribte if one exists, otherwise use the value itself as the description.
        Me._description = If(attributes.Length = 0, _
                             value.ToString(), _
                             attributes(0).Description)
    End Sub
 
    ''' <summary>
    ''' Overridden.  Creates a string representation of the object.
    ''' </summary>
    ''' <returns>
    ''' The friendly description of the value.
    ''' </returns>
    Public Overrides Function ToString() As String
        Return Me.Description
    End Function
 
End Class

这是我刚刚写的一个简化版本:

Imports System.ComponentModel
Imports System.Reflection

Public Class EnumDescriptor(Of T)

    Public ReadOnly Property Description As String

    Public ReadOnly Property Value As T

    Public Sub New(value As T)
        Me.Value = value

        'Get the Description attribute.
        Dim field = value.GetType().GetField(value.ToString())
        Dim attributes = DirectCast(field.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())

        'Use the Description attribute if one exists, otherwise use the value itself as the description.
        Description = If(attributes.Length = 0, value.ToString(), attributes(0).Description)
    End Sub

    Public Overrides Function ToString() As String
        Return Description
    End Function

End Class

这是另一个类,可用于为 Enum 中的每个值存储前一个类的实例:

''' <summary>
''' A collection of EnumDescriptors for an enumerated type.
''' </summary>
''' <typeparam name="T">
''' The type of the enumeration for which the EnumDescriptors are created.
''' </typeparam>
Public Class EnumDescriptorCollection(Of T)
    Inherits ObjectModel.Collection(Of EnumDescriptor(Of T))
 
    ''' <summary>
    ''' Creates a new instance of the <b>EnumDescriptorCollection</b> class.
    ''' </summary>
    Public Sub New()
        'Populate the collection with an EnumDescriptor for each enumerated value.
        For Each value As T In [Enum].GetValues(GetType(T))
            Items.Add(New EnumDescriptor(Of T)(value))
        Next
    End Sub
 
End Class

以下是将集合类的实例绑定到 ComboBox 的一些示例用法:

Public Class Form1
 
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        With ComboBox1
            .DisplayMember = "Description"
            .ValueMember = "Value"
            .DataSource = New EnumDescriptorCollection(Of Sandwiches)
        End With
    End Sub
 
    Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
        MessageBox.Show(ComboBox1.SelectedValue.ToString(), ComboBox1.Text)
    End Sub
 
End Class

答案 1 :(得分:1)

您的目标似乎是覆盖 Enum 值的字符串表示形式。基本表示显示 Enum 的名称,而您希望显示 Enum 的描述属性。

假设这是一个 WinForm 的组合框,标准的方法是用自定义的 TypeConverter 装饰 Enum 并覆盖 ConvertTo 方法。使用 EnumConverter Class 和自定义转换器的基础可以最大限度地减少所需的代码。

Imports System.ComponentModel
Imports System.Globalization
Imports System.Reflection

Public Class EnumDescriptionConverter(Of T As {Structure, IConvertible}) : Inherits EnumConverter

  Private enumDescriptions As New Dictionary(Of T, String)

  Public Sub New()
    MyBase.New(GetType(T))
    ' Since the ability to add an Enum constraint to the generic type does not exist in VB.
    ' this check is to ensure that it is an Enum.
    ' Note that normally, throwing an exception in a constructor is considered bad form, but
    ' there is no other option
    If GetType(T).IsEnum Then
      LoadEnumDescriptions()
    Else
      Throw New ArgumentException($"{GetType(T).Name} is not an Enum")
    End If
  End Sub

  Private Sub LoadEnumDescriptions()
    ' An Enum type comprises static (Shared) fields that represent the defined enum values and
    ' an instance field that holds thae actual value. so only retrieve the static fields
    ' to get the defined (named) enum members.
    For Each fi As FieldInfo In GetType(T).GetFields(BindingFlags.Public Or BindingFlags.Static)
      Dim description As String
      Dim descAttrib As DescriptionAttribute = fi.GetCustomAttribute(Of DescriptionAttribute)
      If descAttrib Is Nothing Then
        ' no description attribute so use the defined name
        description = fi.Name
      Else
        description = descAttrib.Description
      End If
      enumDescriptions.Add(CType(fi.GetValue(Nothing), T), description)
    Next
  End Sub

  Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As CultureInfo, value As Object, destinationType As Type) As Object
    Dim ret As Object

    ' the purpose of this converter is to provide an enum's description attribute as the string representation
    ' instead of the field name as provided by the standard implemention.
    If destinationType Is GetType(String) AndAlso value IsNot Nothing Then
      Dim enumValue As T = DirectCast(value, T)
      If enumDescriptions.ContainsKey(enumValue) Then
        ret = enumDescriptions(enumValue)
      Else
        ret = enumValue.ToString(Nothing)
      End If
    Else
      ret = MyBase.ConvertTo(context, culture, value, destinationType)
    End If
    Return ret
  End Function
End Class

修改您的 Enum 定义以使用此自定义转换器。

<TypeConverter(GetType(EnumDescriptionConverter(Of Sandwiches)))>
Public Enum Sandwiches
  <ComponentModel.Description("Ham Sandwich")> HamSandwich
  <ComponentModel.Description("Reuben")> Reuben
  <ComponentModel.Description("Po’ Boy")> PoBoy
  <ComponentModel.Description("Grilled Cheese")> GrilledCheese
End Enum

现在您需要做的就是使用枚举值加载 ComboBox.Items

ComboBox1.Items.AddRange(System.Enum.GetValues(GetType(Sandwiches)).Cast(Of Object).ToArray())

enter image description here

使用 ComboBox.SelectedItem 检索选定的值。

相关问题