5
\$\begingroup\$

I had to jump through some hoops to get a localized enum working. I went through dozens of SO articles that never actually answered the relevant conundrum with a working solution. So I made my own:

I have two resource files: LocalizedStrings.resx and LocalizedStrings.es-MX.resx With two corresponding lines:

GENDER_FEMALE   Female

GENDER_MALE     Male

... and ...

GENDER_FEMALE   Mujer

GENDER_MALE     Hombre

… respectively.

I have an Enum Extension:

public static class EnumExtensionMethods
{
    public static string GetDisplay(this Enum enumValue)
    {
        object[] attr = enumValue.GetType().GetField(enumValue.ToString())
            .GetCustomAttributes(typeof(DisplayAttribute), false);

        return attr.Length > 0
           ? ((DisplayAttribute)attr[0]).GetName()
           : enumValue.ToString();
    }
}

My actual Enum:

public enum Gender
{
    [Display(ResourceType = typeof(LocalizedStrings), Name = "GENDER_MALE")]
    Male = 1,
    [Display(ResourceType = typeof(LocalizedStrings), Name = "GENDER_FEMALE")]
    Female = 0
}

I load the values into a Winforms ComboBox’s items, (I could not get binding it to an entity to work) with the OnLoad event:

foreach (Gender value in Enum.GetValues(typeof(Gender)))
{
    GenderComboBox.Items.Add(new KeyValuePair<string, Gender>(value.GetDisplay(), value));
}

GenderComboBox.DisplayMember = "Key";
GenderComboBox.ValueMember = "Value";
GenderComboBox.SelectedIndex = 1;

Finally, I have to set the entity value with this event:

private void GenderComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
    if (!GenderComboBox.Focused)
        return;

    if (GenderComboBox.SelectedIndex == -1)
        return;

    KeyValuePair<string, Gender> gender = (KeyValuePair<string, Gender>) GenderComboBox.SelectedItem;

    Person person = (Person)PersonBindingSource.Current;

    person.Gender = gender.Value;
}

All of the previous code just to get a Localized Enum. Is there not a better way?

\$\endgroup\$
3
\$\begingroup\$

You really shouldn't mix event and data-binding together.

The proper way of updating value from UI, would be using ComboBox.DataBindings.Add(Binding). And, not patching it via ComboBox.SelectedSomethingChanged.

Here is how I would do it :

// mock model
var person = new Person { Gender = Gender.Female };;

// mock data source
var personBindingSource = new BindingSource();
personBindingSource.DataSource = new List<Person> { person };

// initialize
var combo = new ComboBox();
// you may want to set DropDownStyle to ComboBoxStyle.DropDownList
// from the designer to disable text input.
//
// choose one of the two below :
//    combo.DisplayMember = "Value";
//    combo.ValueMember = "Key";
//    combo.DataSource = EnumHelper.GetEnumDefinitions<Gender>();
combo.BindTo<Gender>();

// databind
combo.DataBindings.Add(new Binding(
    nameof(combo.SelectedValue), personBindingSource, nameof(person.Gender), false, DataSourceUpdateMode.OnPropertyChanged));

public static class EnumHelper
{
    public static IList GetEnumDefinitions<TEnum>()
        where TEnum : struct, IConvertible
    {
        var type = typeof(TEnum);
        if (!type.IsEnum)
            throw new ArgumentException($"'{type}' is not an enum type.");

        return type
            .GetFields(BindingFlags.Public | BindingFlags.Static)
            .ToDictionary(x => (TEnum)x.GetValue(null), x =>
                x.GetCustomAttribute<DisplayAttribute>()?.GetName() ?? x.GetValue(null).ToString()
            )
            .ToList();
    }

    public static void BindTo<TEnum>(this ComboBox combo)
        where TEnum : struct, IConvertible
    {
        combo.DisplayMember = "Value";
        combo.ValueMember = "Key";
        combo.DataSource = EnumHelper.GetEnumDefinitions<TEnum>();
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.