Lokalizovatelný výčtový typ s podporou DataBindingu
Přidáno: 17.3.2009
Kategorie: Aplikace
Autor: Ondřej Linhart
Jeden z problémů při vytváření uživatelského rozhraní ve Windows Forms je prezentace výčtového typu v nějakém ovládacím prvku typu seznam (nejčastěji ComboBox) tak, aby byl uživatelsky srozumitelný (čitelný) a zároveň lokalizovatelný a velmi snadno použitelný. Vzhledem k poměrné rozsáhlosti kódu se vyhýbám komentářům v lokalizačních třídách.
Použití:
1) Definujte výčtový typ a označte ho atributem <TypeConverter(GetType(LocalizedEnumConverter))>
2) Přiřaďte výčtový typ jako zdroj dat pro ovládací prvek pomocí [Enum].GetValues(GetType(VýčtovýTyp))
3) V Resource souborech pro příslušné kultury proveďte samotnou lokalizaci. Jméno položky je ve tvaru NázevVýčtu_HodnotaVýčtu, hodnota položky je lokalizovaný textový řetězec. Pokud nebude nalezen lokalizovaný řetězec pro požadovanou kulturu, použije se výchozí název položky výčtového typu.
Lokalizovaný textový řetězec se vybere automaticky na základě kultury pro uživatelské rozhraní aktuálního vlákna (My.Application.UICulture).
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Globalization
Imports System.Resources
Public Class ResourceEnumConverter
Inherits EnumConverter
Private Class LookupTable
Inherits Dictionary(Of String, Object)
End Class
Private flagValues As Array
Private isFlagEnum As Boolean
Private lookupTables As Dictionary(Of CultureInfo, LookupTable)
Private resourceManager As ResourceManager
Private Function IsSingleBitValue(ByVal value As Integer) As Boolean
Select Case value
Case 0
Return False
Case 1
Return True
Case Else
Return (value And (value - 1)) = 0
End Select
End Function
Private Function GetFlagValue(ByVal culture As CultureInfo, ByVal text As String) As Object
Dim lookupTable As LookupTable = GetLookupTable(culture)
Dim textValues As String() = text.Split(New String() {culture.TextInfo.ListSeparator}, StringSplitOptions.None)
Dim result As Integer
Dim value As Object = Nothing
For Each textValue As String In textValues
If Not lookupTable.TryGetValue(textValue, value) Then
Return Nothing
End If
result = result Or Convert.ToInt32(value)
Next
Return System.Enum.ToObject(Me.EnumType, result)
End Function
Private Function GetFlagValueText(ByVal culture As CultureInfo, ByVal value As Object) As String
If System.Enum.IsDefined(value.GetType(), value) Then
Return GetValueText(culture, value)
End If
Dim flagValue2 As Integer
Dim result As String
Dim valueText As String
For Each flagValue As Object In flagValues
flagValue2 = Convert.ToInt32(flagValue)
If IsSingleBitValue(flagValue2) Then
If (flagValue2 And Convert.ToInt32(value)) = flagValue2 Then
valueText = GetValueText(culture, flagValue)
If result Is Nothing Then
result = valueText
Else
result = result & culture.TextInfo.ListSeparator & valueText
End If
End If
End If
Next
Return result
End Function
Private Function GetLookupTable(ByVal culture As CultureInfo) As LookupTable
If culture Is Nothing Then
culture = My.Application.Culture
End If
Dim result As LookupTable
If Not lookupTables.TryGetValue(culture, result) Then
result = New LookupTable
For Each value As Object In Me.GetStandardValues()
Dim valueText As String = GetValueText(culture, value)
If valueText IsNot Nothing Then
result.Add(valueText, value)
End If
Next
lookupTables.Add(culture, result)
End If
Return result
End Function
Private Function GetValue(ByVal culture As CultureInfo, ByVal text As String) As Object
Dim lookupTable As LookupTable = GetLookupTable(culture)
Dim result As Object = Nothing
lookupTable.TryGetValue(text, result)
Return result
End Function
Private Function GetValueText(ByVal culture As CultureInfo, ByVal value As Object) As String
Dim name As String = value.GetType().Name & "_" & value.ToString()
Dim result As String = resourceManager.GetString(name, culture)
If result Is Nothing Then
Return value.ToString()
End If
Return result
End Function
Public Overloads Shared Function ConvertToString(ByVal value As System.Enum) As String
If value Is Nothing Then
Throw New ArgumentNullException("value")
End If
Return TypeDescriptor.GetConverter(value.GetType()).ConvertToString(value)
End Function
Public Shared Function GetValues(ByVal enumType As Type) As List(Of KeyValuePair(Of System.Enum, String))
Return GetValues(enumType, My.Application.Culture)
End Function
Public Shared Function GetValues(ByVal enumType As Type, ByVal culture As CultureInfo) As List(Of KeyValuePair(Of System.Enum, String))
If enumType Is Nothing Then
Throw New ArgumentNullException("enumType")
End If
If culture Is Nothing Then
Throw New ArgumentNullException("culture")
End If
Dim result As New List(Of KeyValuePair(Of System.Enum, String))
Dim converter As TypeConverter = TypeDescriptor.GetConverter(enumType)
For Each value As System.Enum In System.Enum.GetValues(enumType)
Dim item As New KeyValuePair(Of System.Enum, String)(value, converter.ConvertToString(Nothing, culture, value))
result.Add(item)
Next
Return result
End Function
Public Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object) As Object
If culture Is Nothing Then
Throw New ArgumentNullException("culture")
End If
If value Is Nothing Then
Throw New ArgumentNullException("value")
End If
If TypeOf value Is String Then
Dim result As Object = IIf(isFlagEnum, GetFlagValue(culture, DirectCast(value, String)), GetValue(culture, DirectCast(value, String)))
If result Is Nothing Then
result = MyBase.ConvertFrom(context, culture, value)
End If
Return result
Else
Return MyBase.ConvertFrom(context, culture, value)
End If
End Function
Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
If culture Is Nothing Then
Throw New ArgumentNullException("culture")
End If
If value Is Nothing Then
Throw New ArgumentNullException("value")
End If
If destinationType Is Nothing Then
Throw New ArgumentNullException("destinationType")
End If
If destinationType Is GetType(String) Then
Return IIf(isFlagEnum, GetFlagValueText(culture, value), GetValueText(culture, value))
Else
Return MyBase.ConvertTo(context, culture, value, destinationType)
End If
End Function
Public Sub New(ByVal type As Type, ByVal resourceManager As ResourceManager)
MyBase.New(type)
If resourceManager Is Nothing Then
Throw New ArgumentNullException("resourceManager")
End If
Me.resourceManager = resourceManager
lookupTables = New Dictionary(Of CultureInfo, LookupTable)
Dim flagAttributes As Object() = type.GetCustomAttributes(GetType(FlagsAttribute), True)
isFlagEnum = flagAttributes.Length > 0
If isFlagEnum Then
flagValues = System.Enum.GetValues(type)
End If
End Sub
End Class
Public Class LocalizedEnumConverter
Inherits ResourceEnumConverter
Public Sub New(ByVal type As Type)
MyBase.New(type, My.Resources.ResourceManager)
End Sub
End Class
Public Class Form1
<TypeConverter(GetType(LocalizedEnumConverter))> _
Private Enum Protocol
Tcp = 0
Http = 1
End Enum
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
ComboBox1.DataSource = [Enum].GetValues(GetType(Protocol))
End Sub
End Class