Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

wpf - Show enum in a comboBox

<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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...