Localized enums with Entity Framework Code-First 4.1 in WPF MVVM and ASP.NET MVC 3. Part 2)

By Mirek on (tags: ASP.NET MVC, Code First, Entity Framework, enums, localization, mvvm, WPF, categories: code)

 

Part 2. Displaying localized names of enum values in WPF application

To achieve the human readable enums I have performed following steps.

  1. For each language I want to support I have added a resource (.resx) file with key equals the string representation of the enum value.
    resources_thumb5 
  2. Then we decorate our enum values with Display attribute as follows

       1: public enum Status
       2: {
       3:     [Display(Description = "Online", ResourceType = typeof(Resources.Resources))]
       4:     Online,
       5:     [Display(Description = "Busy", ResourceType = typeof(Resources.Resources))]
       6:     Busy,
       7:     [Display(Description = "Away", ResourceType = typeof(Resources.Resources))]
       8:     Away,
       9:     [Display(Description = "Offline", ResourceType = typeof(Resources.Resources))]
      10:     Offline
      11: }


    As a description we can provide a strict value that will be displayed or, when setting also RepositoryType, we provide the resource key which will be used to find localized description in a resources.
  3. Now, when every enum value has its display attribute it is easy to get its value  using following extension method

       1: public static string GetLocalizedName(this Enum @enum)
       2: {
       3:     if (@enum == null)
       4:         return null;
       5:     string description = @enum.ToString();
       6:     FieldInfo fieldInfo = @enum.GetType().GetField(description);
       7:  
       8:     var attributes =
       9:         (DisplayAttribute[])fieldInfo.GetCustomAttributes(typeof(DisplayAttribute), false);
      10:  
      11:     if (attributes.Any())
      12:     {
      13:         description = attributes[0].GetDescription();
      14:     }
      15:  
      16:     return description;
      17: }


  4. Then I have added custom TypeConverter to convert enum type to its localized name

       1: public class EnumToLocalizedName : TypeConverter
       2: {
       3: public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
       4: {
       5: return (sourceType.Equals(typeof(Enum)));
       6: }
       7: public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
       8: {
       9: return (destinationType.Equals(typeof(String)));
      10: }
      11: public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
      12: {
      13: if (!(value is Enum))
      14: {
      15: throw new ArgumentException(@"Can only convert an instance of enum.", "value");
      16: }
      17: return ((Enum)value).GetDescription();
      18: }
      19: public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
      20: {
      21: if (!destinationType.Equals(typeof(String)))
      22: {
      23: throw new ArgumentException(@"Can only convert to string.", "destinationType");
      24: }
      25: if (!(value is Enum))
      26: {
      27: throw new ArgumentException(@"Can only convert an instance of enum.", "value");
      28: }
      29: return ((Enum)value).GetDescription();
      30: }
      31: }


    and decorated enum values with this converter.

   1: [TypeConverter(typeof(EnumToLocalizedName))]
   2: public enum Status
   3: {
   4:     [Display(Description = "Online", ResourceType = typeof(Resources.Resources))]
   5:     Online,
   6:     [Display(Description = "Busy", ResourceType = typeof(Resources.Resources))]
   7:     Busy,
   8:     [Display(Description = "Away", ResourceType = typeof(Resources.Resources))]
   9:     Away,
  10:     [Display(Description = "Offline", ResourceType = typeof(Resources.Resources))]
  11:     Offline
  12: }



Custom type converter is a very useful approach that facilitates binding enum values in WPF applications without any conversion or additional operations.

Ok. It seems to be a mass of strange classes, but you will see later that they are quite useful. Anyway for each new enum type you just need to decorate this type with EnumToLocalizedName converter and LocalizedDescription attributes and everything works in WPF and as you will later see also in MVC 3 application.

If we now want to bind a list of our Client objects described in Part 1, exposed in ViewModel as an observable collection

   1: public ObservableCollection<ChatClient> Clients

to the view in DataGrid component, we just do following

   1: <DataGrid AutoGenerateColumns="False" Name="dgChatClients"
   2: ItemsSource="{Binding Path=Clients}" >
   3: <DataGrid.Columns>
   4: <DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="False"/>
   5: <DataGridTextColumn Header="Status" Binding="{Binding Path=Status}" IsReadOnly="True"/>
   6: </DataGrid.Columns>
   7: </DataGrid>

and that’s it. We have localized values in column Status for each Client. Thanks to the type converter the displayed values are properly localized according to the current culture. If we now want to change the language to Polish then it is as simple as writing

   1: Thread.CurrentThread.CurrentUICulture =
   2: CultureInfo.CreateSpecificCulture("pl-PL");

and reloading bindable properties.

Additionally if we want to get the list of all possible values of particular Enum type we do it following way

   1: ObservableCollection<Status> Statuses =
   2: new ObservableCollection<Status>(Enum.GetValues(typeof(Status)).Cast<Status>());