1
<Grid xmlns:local="clr-namespace:SortedEnumInComboBox" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"> <Grid.Resources> <CollectionViewSource x:Key="ComparatorsView"> <CollectionViewSource.Source> <ObjectDataProvider MethodName="GetNames" ObjectType="{x:Type sys:Enum}"> <ObjectDataProvider.MethodParameters> <x:Type TypeName="local:Comparators"/> </ObjectDataProvider.MethodParameters> </ObjectDataProvider> </CollectionViewSource.Source> </CollectionViewSource> </Grid.Resources> <ComboBox DataContext="{StaticResource ComparatorsView}" ItemsSource="{Binding}" VerticalAlignment="Center" /> 

public enum Comparators { MinorThan = -1, Equals = 0, GreaterThan = 1 } 

I'm trying to set the enum in a comboBox. In Display must show <, =, and > and SelectedValue will be Comparators value.

I'm not sure if am I use a converter or what?

3
  • Check this answer stackoverflow.com/a/4398752/416497 Commented Feb 11, 2012 at 17:45
  • Did you find a solution? I have a converter/attribute solution but there's a fair bit of code. Commented Feb 16, 2012 at 14:55
  • No, I didn't. Is possible that you post it? Commented Feb 16, 2012 at 20:46

1 Answer 1

2

This solution - which was not created by me - allows you to associate localized strings with enum values. So assuming we have an enum defined as

[EnumResourceManagerForLabels(typeof(Resources))] public enum Operator { EqualTo = 0, GreaterThan = 1, LessThan = -1 } 

Step 1: Add a Windows resource (.resx) file called in this case Resources.resx

Step 2: Add string resources to the file of the form EnumTypeName_EnumName for each enum value, e.g:

Operator_EqualTo = Operator_GreaterThan > Operator_LessThan < 

Now we need the EnumResourceManagerForLabels attribute which is used by our converter so it know which resource file to use (see usage above):

[AttributeUsage(AttributeTargets.Enum)] public sealed class EnumResourceManagerForLabelsAttribute : Attribute { private readonly Type m_resourceManagerType; public EnumResourceManagerForLabelsAttribute(Type resourceManagerType) { m_resourceManagerType = resourceManagerType; } public Type ResourceManagerType { get { return m_resourceManagerType; } } } 

Now we need the converter:

/// <summary> /// An <see cref="IValueConverter"/> converter that converts between strings and enum types. /// </summary> public class EnumLabelConverter : IValueConverter { private readonly Dictionary<object, string> m_labelsByValue = new Dictionary<object, string>(); private readonly SortedDictionary<string, object> m_valuesByLabel = new SortedDictionary<string, object>(StringComparer.Ordinal); private Type m_enumType; private bool m_labelsAreUnique; private bool m_sortDisplayNamesAlphanumerically = true; public Type EnumType { get { return m_enumType; } set { if (value == null) { throw new ArgumentNullException("value"); } if (m_enumType != value) { m_valuesByLabel.Clear(); m_labelsByValue.Clear(); m_labelsAreUnique = true; m_enumType = value; if (m_enumType.IsEnum) { const bool lookAtInheritedAttributes = false; // Ignored. var enumResourceManagerAttribute = m_enumType.GetCustomAttributes(typeof (EnumResourceManagerForLabelsAttribute), lookAtInheritedAttributes).FirstOrDefault() as EnumResourceManagerForLabelsAttribute; ResourceManager manager; if (enumResourceManagerAttribute != null) { manager = new ResourceManager(enumResourceManagerAttribute.ResourceManagerType); } else { // We use the invariant culture for detailing exceptions caused by programmer error. throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The enum '{0}' does not have a '{1}' attribute.", m_enumType.FullName, typeof (EnumResourceManagerForLabelsAttribute). FullName), "value"); } // For each field, retrieve the label from the resource manager. foreach (FieldInfo fieldInfo in m_enumType.GetFields(BindingFlags.Public | BindingFlags.Static)) { // We use the invariant culture to compute the string to use as the key to the resource. string resourceKey = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", m_enumType.Name, fieldInfo.Name); string description = manager.GetString(resourceKey); if (string.IsNullOrEmpty(description)) { // We use the invariant culture for detailing exceptions caused by programmer error. throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "The label for the field {0} of the enum {1} is either null, empty, or the string resource with key {2} is absent from the {3} resource file.", fieldInfo.Name, m_enumType.FullName, resourceKey, manager.BaseName)); } object fieldValue = fieldInfo.GetValue(null); if (m_valuesByLabel.ContainsKey(description)) { // We already have an entry with that label so we cannot provide ConvertBack() // functionality because of the ambiguity. m_labelsAreUnique = false; } else { m_valuesByLabel.Add(description, fieldValue); } m_labelsByValue.Add(fieldValue, description); } } else { // We use the invariant culture for detailing exceptions caused by programmer error. throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The type '{0}' is not an enum type.", m_enumType.Name), "value"); } } } } /// <summary> /// Gets or sets whether the <see cref="DisplayNames"/> are to be sorted alphanumerically. /// </summary> /// <value><b>true</b> if the <see cref="DisplayNames"/> are to be sorted alphanumerically; otherwise, <b>false</b>.</value> public bool SortDisplayNamesAlphanumerically { get { return m_sortDisplayNamesAlphanumerically; } set { m_sortDisplayNamesAlphanumerically = value; } } /// <summary> /// Gets the display names (labels) for the fields of the associated enum type. /// </summary> /// <value>The display names.</value> public IEnumerable<string> DisplayNames { get { return (SortDisplayNamesAlphanumerically) ? m_valuesByLabel.Keys : m_labelsByValue.Values as IEnumerable<string>; } } #region IValueConverter Members /// <summary> /// Converts the enum value into a string. /// </summary> /// <param name="value">The value.</param> /// <param name="targetType">Type of the target.</param> /// <param name="parameter">The parameter.</param> /// <param name="culture">The culture.</param> public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // We intentionally do not assert anything about 'value', so that we fail gracefully if the binding was not hooked up correctly. // (this may be the case when first loading some UI). object result = DependencyProperty.UnsetValue; if (value != null) { // See if we have been given a single value or a collection of values. var values = value as IEnumerable; if (values != null) { var labels = new List<string>(); foreach (object item in values) { string labelString; if (m_labelsByValue.TryGetValue(item, out labelString)) { labels.Add(labelString); } else { throw new NotSupportedException(); } } result = labels; } else { string labelString; result = m_labelsByValue.TryGetValue(value, out labelString) ? labelString : DependencyProperty.UnsetValue; } } return result; } /// <summary> /// Converts the string back into an enum of the appropriate type. /// </summary> /// <param name="value">The value.</param> /// <param name="targetType">Type of the target.</param> /// <param name="parameter">The parameter.</param> /// <param name="culture">The culture.</param> public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (!m_labelsAreUnique) { throw new InvalidOperationException( GetType().Name + ".ConvertBack() requires that enum labels are unique to avoid ambiguity."); } // We intentionally do not assert anything about 'value', so that we fail gracefully if the binding was not hooked up correctly, // (this may be the case when first loading some UI). object enumValue; var labelString = value as string; if (!string.IsNullOrEmpty(labelString)) { if (!m_valuesByLabel.TryGetValue(labelString, out enumValue)) { // The value for the label could not be found. enumValue = DependencyProperty.UnsetValue; } } else { // The value we were passed was a null or empty string, or not a string. enumValue = DependencyProperty.UnsetValue; } return enumValue; } #endregion } 

and finally an example of usage:

<Grid> <Grid.Resources> <Converters:EnumLabelConverter x:Key="OperatorConverter" EnumType="{x:Type ViewModel:Operator}" /> </Grid.Resources> <StackPanel> <!-- Bind text block to 'GreaterThan' --> <TextBlock Text="{Binding Source={x:Static ViewModel:Operator.GreaterThan}, Converter={StaticResource OperatorConverter}}"/> <!-- Bind text block to datacontext --> <TextBlock Text="{Binding SelectedOperator, Converter={StaticResource OperatorConverter}}"/> <!-- Bind combo box to localized enums and bind to datacontext --> <ComboBox SelectedValue="{Binding SelectedOperator, Converter={StaticResource OperatorConverter}}" ItemsSource="{Binding Source={StaticResource OperatorConverter}, Path=DisplayNames}"/> <!-- Bind combo box to localized enums and select 'GreaterThan' --> <ComboBox SelectedValue="{Binding Source={x:Static ViewModel:Operator.GreaterThan}, Converter={StaticResource OperatorConverter}, Mode=OneWay}" ItemsSource="{Binding Source={StaticResource OperatorConverter}, Path=DisplayNames}"/> </StackPanel> </Grid> 

Where the view model (using MVVM light) is:

public class EnumBindingSampleViewModel : ViewModelBase { private Operator _selectedOperator; public Operator SelectedOperator { get { return _selectedOperator; } set { Set(()=>SelectedOperator, ref _selectedOperator, value); } } } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.