Snippet

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
  'Definice lokalizovatelného výčtového typu.
  <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
    'Přiřazení lokalizovaného výčtového typu
    'jako zdroj dat pro ComboBox.
    ComboBox1.DataSource = [Enum].GetValues(GetType(Protocol))
  End Sub
End Class

'Anglická (výchozí) lokalizace výčtového typu
'v souboru Resources.resx:
'<data name="Protocol_Tcp" xml:space="preserve">
'  <value>Use TCP protocol</value>
'</data>
'<data name="Protocol_Http" xml:space="preserve">
'  <value>Use HTTP protocol</value>
'</data>

'Česká lokalizace výčtového typu
'v souboru Resources.cs.resx:
'<data name="Protocol_Tcp" xml:space="preserve">
'  <value>Použít protokol TCP</value>
'</data>
'<data name="Protocol_Http" xml:space="preserve">
'  <value>Použít protokol HTTP</value>
'</data> 
 

VBNET.CZ | © 2007 Tomáš Herceg, Tomáš Jecha | Kopírování a přejímání jakéhokoliv obsahu z tohoto webu je bez písemného svolení autorů zakázáno.