7

Related: Get enum from enum attribute

I want the most maintainable way of binding an enumeration and it's associated localized string values to something.

If I stick the enum and the class in the same file I feel somewhat safe but I have to assume there is a better way. I've also considered having the enum name be the same as the resource string name, but I'm afraid I can't always be here to enforce that.

using CR = AcmeCorp.Properties.Resources;

public enum SourceFilterOption
{
    LastNumberOccurences,
    LastNumberWeeks,
    DateRange
    // if you add to this you must update FilterOptions.GetString
}

public class FilterOptions
{
    public Dictionary<SourceFilterOption, String> GetEnumWithResourceString()
    {
        var dict = new Dictionary<SourceFilterOption, String>();
        foreach (SourceFilterOption filter in Enum.GetValues(typeof(SourceFilterOption)))
        {
            dict.Add(filter, GetString(filter));
        }
        return dict;
    }

    public String GetString(SourceFilterOption option)
    {
        switch (option)
        {
            case SourceFilterOption.LastNumberOccurences:
                return CR.LAST_NUMBER_OF_OCCURANCES;
            case SourceFilterOption.LastNumberWeeks:
                return CR.LAST_NUMBER_OF_WEEKS;
            case SourceFilterOption.DateRange:
            default:
                return CR.DATE_RANGE;
        }
    }
}
Community
  • 1
  • 1
JoeB
  • 2,743
  • 6
  • 38
  • 51

5 Answers5

10

You can add DescriptionAttribute to each enum value.

public enum SourceFilterOption
{
    [Description("LAST_NUMBER_OF_OCCURANCES")]
    LastNumberOccurences,
    ...
}

Pull out the description (resource key) when you need it.

FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute),     
if (attributes.Length > 0)
{
    return attributes[0].Description;
}
else
{
    return value.ToString();
}

http://geekswithblogs.net/paulwhitblog/archive/2008/03/31/use-the-descriptionattribute-with-an-enum-to-display-status-messages.aspx

Edit: Response to comments (@Tergiver). Using the (existing) DescriptionAttribute in my example is to get the job done quickly. You would be better implementing your own custom attribute instead of using one outside of its purpose. Something like this:

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inheritable = false)]
public class EnumResourceKeyAttribute : Attribute
{
 public string ResourceKey { get; set; }
}
Paul Fleming
  • 24,238
  • 8
  • 76
  • 113
  • I like this although I would probably create a new attribute like ResourceNameAttribute. I'll leave it open for a while to see if anything better comes long, but I'm already updating my code. Thank you. – JoeB Jul 17 '12 at 22:51
  • Uh, downvote, care to explain? Does it not work? Because it's better than my solution. – JoeB Jul 17 '12 at 22:52
  • +1 though I too would prefer not abusing an existing attribute for this – Tergiver Jul 17 '12 at 23:44
  • Ugh, this doesn't work for my particular use case (localized strings). When I do: public enum SourceFilterOption { [LocalizedString(CR.LAST_NUMBER_OF_OCCURANCES)] LastNumberOccurences } I get "An attribute argument must be a constant". – JoeB Jul 17 '12 at 23:52
  • 1
    @Joe, use the CONSTANT resource key in the attribute. Attribute parameters must be constant. When you get the value out of the attribute at runtime, use that value as the key to your resource file. – Paul Fleming Jul 18 '12 at 07:57
  • This worked by string the NAME of the resource, not the resource itself. Thanks again. – JoeB Jul 23 '12 at 17:55
  • +1 Put this in an extension method to Enum object - works like a charm, thanks! – Niklas Wulff Mar 26 '13 at 16:02
  • @NiklasRingdahl. We do the same :). As JoeB mentioned, it would be nice to create a custom attribute rather than jumping on the back of the existing one with a different original purpose. – Paul Fleming Mar 27 '13 at 09:05
1

You could just crash immediately if someone doesn't update it correctly.

public String GetString(SourceFilterOption option)
{
    switch (option)
    {
        case SourceFilterOption.LastNumberOccurences:
            return CR.LAST_NUMBER_OF_OCCURANCES;
        case SourceFilterOption.LastNumberWeeks:
            return CR.LAST_NUMBER_OF_WEEKS;
        case SourceFilterOption.DateRange:
            return CR.DATE_RANGE;
        default:
            throw new Exception("SourceFilterOption " + option + " was not found");
    }
}
Mike Blandford
  • 3,952
  • 4
  • 29
  • 32
  • +1 for the exception in the default case. I hate to see default cases in switch/case statements for enums to return a (valid) value. This can really hide bugs... – Tobias May 18 '15 at 16:01
1

I do mapping to resourse in the following way: 1. Define a class StringDescription with ctor getting 2 parameters (type of resourse and it's name)

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
class StringDescriptionAttribute : Attribute
{
    private string _name;
    public StringDescriptionAttribute(string name)
    {
        _name = name;
    }

    public StringDescriptionAttribute(Type resourseType, string name)
    {
            _name = new ResourceManager(resourseType).GetString(name);

    }
    public string Name { get { return _name; } }
}
  1. Create a resourse file for either culture (for example WebTexts.resx and Webtexts.ru.resx). Let is be colours Red, Green, etc...

  2. Define enum:

    enum Colour{ [StringDescription(typeof(WebTexts),"Red")] Red=1 , [StringDescription(typeof(WebTexts), "Green")] Green = 2, [StringDescription(typeof(WebTexts), "Blue")] Blue = 3, [StringDescription("Antracit with mad dark circles")] AntracitWithMadDarkCircles

    }

  3. Define a static method getting resource description via reflection

    public static string GetStringDescription(Enum en) {

        var enumValueName = Enum.GetName(en.GetType(),en);
    
        FieldInfo fi = en.GetType().GetField(enumValueName);
    
        var attr = (StringDescriptionAttribute)fi.GetCustomAttribute(typeof(StringDescriptionAttribute));
    
        return attr != null ? attr.Name : "";
    }
    
  4. Eat :

    Colour col; col = Colour.Red; Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");

        var ListOfColors = typeof(Colour).GetEnumValues().Cast<Colour>().Select(p => new { Id = p, Decr = GetStringDescription(p) }).ToList();
    
        foreach (var listentry in ListOfColors)
            Debug.WriteLine(listentry.Id + " " + listentry.Decr);
    
Lapenkov Vladimir
  • 3,066
  • 5
  • 26
  • 37
1

There is the simplest way to getting the enum description value according to culture from resources files

My Enum

  public enum DiagnosisType
    {
        [Description("Nothing")]
        NOTHING = 0,
        [Description("Advice")]
        ADVICE = 1,
        [Description("Prescription")]
        PRESCRIPTION = 2,
        [Description("Referral")]
        REFERRAL = 3

}

i have made resource file and key Same as Enum Description Value

Resoucefile and Enum Key and Value Image, Click to view resouce file image

   public static string GetEnumDisplayNameValue(Enum enumvalue)
    {
        var name = enumvalue.ToString();
        var culture = Thread.CurrentThread.CurrentUICulture;
        var converted = YourProjectNamespace.Resources.Resource.ResourceManager.GetString(name, culture);
        return converted;
    }

Call this method on view

  <label class="custom-control-label" for="othercare">YourNameSpace.Yourextenstionclassname.GetEnumDisplayNameValue(EnumHelperClass.DiagnosisType.NOTHING )</label>

It will return the string accordig to your culture

Mahaveer Jangid
  • 366
  • 2
  • 11
0

I've to use it in WPF here is how I achieve it

First of all you need to define an attribute

public class LocalizedDescriptionAttribute : DescriptionAttribute
{
    private readonly string _resourceKey;
    private readonly ResourceManager _resource;
    public LocalizedDescriptionAttribute(string resourceKey, Type resourceType)
    {
        _resource = new ResourceManager(resourceType);
        _resourceKey = resourceKey;
    }

    public override string Description
    {
        get
        {
            string displayName = _resource.GetString(_resourceKey);

            return string.IsNullOrEmpty(displayName)
                ? string.Format("[[{0}]]", _resourceKey)
                : displayName;
        }
    }
}

You can use that attribute like this

public enum OrderType
{
    [LocalizedDescription("DineIn", typeof(Properties.Resources))]
    DineIn = 1,
    [LocalizedDescription("Takeaway", typeof(Properties.Resources))]
    Takeaway = 2
}

Then Define a converter like

public class EnumToDescriptionConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo language)
    {
        var enumValue = value as Enum;

        return enumValue == null ? DependencyProperty.UnsetValue : enumValue.GetDescriptionFromEnumValue();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo language)
    {
        return value;
    }
}

Then in your XAML

<cr:EnumToDescriptionConverter x:Key="EnumToDescriptionConverter" />

<TextBlock Text="{Binding Converter={StaticResource EnumToDescriptionConverter}}"/>
dnxit
  • 7,118
  • 2
  • 30
  • 34