【问题标题】:Bind all Descriptions of an enum to a combobox将枚举的所有描述绑定到组合框
【发布时间】:2021-02-12 14:35:06
【问题描述】:

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

我有一个这样的枚举:

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,因为我不知道定义了什么类型变量。

【问题讨论】:

  • This 你应该会感兴趣。
  • 我添加了一个使用上面链接中的代码的答案。
  • OT,Sandwiches 是那个 Enum 的错误名称。应该是Sandwich。当且仅当它应用了Flags 属性时,您应该将Enum 的名称复数。如果您希望能够创建复合值,则可以使用该属性,从而实现多元化。如果您一次只使用一个值,那么您使用一个单数名称。
  • dim attr = GetType(Sandwiches).GetFields().Select(Function(f) f.GetCustomAttribute(Of DescriptionAttribute)?.Description).ToArray()。这是一个顺序枚举,value = SelectedIndex。最终删除第一个空值。
  • @jmcilhinney,很长时间以来,我显然一直在错误地命名我的枚举。谢谢你告诉我!

标签: vb.net enums combobox


【解决方案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

【讨论】:

    【解决方案2】:

    看来您的目标是覆盖 Enum 值的字符串表示形式。基本表示显示 Enum 的名称,您希望显示 Enum 的 Description 属性。

    假设这是一个 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())
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-18
      • 1970-01-01
      • 2020-07-26
      • 2011-08-04
      • 2018-11-12
      • 2020-02-07
      • 1970-01-01
      相关资源
      最近更新 更多