2

Actually I'm able to bind my Combobox to an Enum using their DescriptionAttribute:

<ComboBox ItemsSource="{Binding Source={extension:Enumeration {x:Type model:MyEnum}}}"
      DisplayMemberPath="Description" 
      SelectedValue="{Binding Path=DataContextEnumProp}"
      SelectedValuePath="Value"/>

I've used this SO answer

My enum is :

public enum MyEnum
{
    [XmlEnum("first")]
    [Description("first")]
    FirstEnumMember,

    [XmlEnum("second")]
    [Description("second")]
    SecondEnumMember,

    //[XmlIgnore]
    [Description("second")]
    AlternativeToSecondEnumMember //I don't want this one to be in the combobox
}

I need to hide the AlternativeToSecondEnumMember, because it's irrelevant to see it in the ComboBox.

To do so, what's the best solution? Maybe a Converter or setting the ItemsSource in code behind ?

Community
  • 1
  • 1
ZwoRmi
  • 1,093
  • 11
  • 30

5 Answers5

5

Here is some attribute based solution that used the ObjectDataProvider special implementation. So if an Enum field is decorated by the ShouldBeHiddenAttribute and its value is true, then the enum field will be hidden in UI(ComboBox).

ShouldBeHiddenAttribute code

/// <summary>
/// helps to hide the enum value
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class ShouldBeHiddenAttribute : Attribute
{
    public ShouldBeHiddenAttribute(bool isHiddenInUi)
    {
        HiddenInUi = isHiddenInUi;
    }

    public bool HiddenInUi { get; set; }
}

Your enum decorated with my attribute

public enum MyEnum
{
    [XmlEnum("first")]
    [Description("first")]
    FirstEnumMember,

    [XmlEnum("second")]
    [Description("second")]
    SecondEnumMember,

    //[XmlIgnore]
    [Description("second")]
    [ShouldBeHiddenAttribute(true)]
    AlternativeToSecondEnumMember
    //I don't want this one to be in the combobox
}

Special ObjectDataProvider implementation

/// <summary>
/// helps to display value according to decorating attributes
/// </summary>
public class AttributeBasedObjectDataProvider : ObjectDataProvider
{
    /// <summary>
    /// returns value of enum according its two attributes
    /// 1. DescriptionAttribute - provide a dispaly name of the enum value
    /// 2. ShouldBeHiddenAttribute - provide a dispaly state of the enum
    /// </summary>
    /// <param name="enumObj">enum field value</param>
    /// <returns>if ShouldBeHiddenAttribute.HiddenInUi value is true return null else enum Description if defined(or enum actual value id Description is not defined)</returns>
    public object GetEnumValues(Enum enumObj)
    {
        //get the ShouldBeHiddenAttribute value
        var isHidden = enumObj.GetType().GetRuntimeField(enumObj.ToString()).
            GetCustomAttributes(typeof (ShouldBeHiddenAttribute), false).
            SingleOrDefault() as ShouldBeHiddenAttribute;
        if (isHidden != null && isHidden.HiddenInUi) return null;
        //get the DescriptionAttribute value
        var attribute = enumObj.GetType().GetRuntimeField(enumObj.ToString()).
            GetCustomAttributes(typeof (DescriptionAttribute), false).
            SingleOrDefault() as DescriptionAttribute;
        return attribute == null ? enumObj.ToString() : attribute.Description;
    }

    /// <summary>
    /// returns collection of enum values
    /// </summary>
    /// <param name="type">enum type</param>
    /// <returns>collection of enum values</returns>
    public List<object> GetShortListOfApplicationGestures(Type type)
    {
        var shortListOfApplicationGestures =
            Enum.GetValues(type).OfType<Enum>().Select(GetEnumValues).Where(o => o != null).ToList();
        return
            shortListOfApplicationGestures;
    }
}

Xaml ObjectDataProvider definition

    <ObjectDataProvider x:Key="MyEnumMembers" MethodName="GetShortListOfApplicationGestures" ObjectType="{x:Type pageBasedApp:AttributeBasedObjectDataProvider}">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="pageBasedApp:MyEnum" />
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>

Combo binding

<ComboBox ItemsSource="{Binding Source={StaticResource MyEnumMembers}}" Width="150"/>

Regards.

Ilan
  • 2,762
  • 1
  • 13
  • 24
3

I would suggest to use a simple solution to make your life easier. Assuming you are using MVVM, you can simply create a new property to hold the list of suitable values.

private ObservableCollection<string> _myEnumList;
public ObservableCollection<string> MyEnumList
{
    get
    {
        if (this._myEnumList == null)
        {
            string[] myList = Enum.GetNames(typeof(MyEnum));
            // You can also use your own logic to generate your list


            this._myEnumList = new ObservableCollection<string>(myList);
        }
        return this._myEnumList;
    }
}

Personally I find that there is no point in trying to find a way to auto-convert the Enum type into a list of string values when not all the values in the Enum are valid.

Jai
  • 8,165
  • 2
  • 21
  • 52
  • If doing so, I will have to _convert_ `string` to `MyEnum` in my VM, that's what I want to avoid. – ZwoRmi May 19 '16 at 08:14
  • 1
    @ZwoRmi `ObservableCollection MyEnumList = new ObservableCollection(Enum.GetValues(typeof(MyEnum)).Cast());` – Jinjinov Jun 18 '19 at 12:35
1

Since you wrote that you want to avoid converting string to MyEnum in VM, here is a solution without string:

public ObservableCollection<MyEnum> MyEnumList { get; } = new ObservableCollection<MyEnum>(
    Enum.GetValues(typeof(MyEnum)).
    Cast<MyEnum>().
    Where(myEnum => myEnum != MyEnum.AlternativeToSecondEnumMember));

This way you can keep working with MyEnum, no conversions from string necessary.

Jinjinov
  • 2,554
  • 4
  • 26
  • 45
0

I was able to find a way to do what I want using a Converter and DataTrigger from this solution. Since the link is about IsEnabled and not IsVisible, and for readability, I put what worked for me here.

First I had to add a DataTrigger in my ComboBox:

<ComboBox ItemsSource="{Binding Source={extension:Enumeration {x:Type model:MyEnum}}}" 
          DisplayMemberPath="Description" 
          SelectedValue="{Binding Path=DataContextEnumProp}" 
          SelectedValuePath="Value">
    <ComboBox.ItemContainerStyle>
        <Style TargetType="ComboBoxItem">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource Self}, 
                             Converter={StaticResource ComboBoxVisibleConverter}}" Value="true">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ComboBox.ItemContainerStyle>
</ComboBox>

Then to create a ComboBoxVisibleConverter :

class ComboBoxVisibleConverter :IValueConverter
{
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
     {
        EnumerationExtension.EnumerationMember enumerationMember = value as EnumerationExtension.EnumerationMember;
        if (enumerationMember == null )
            return null;
        if ((MyEnum)enumerationMember.Value == MyEnum.AlternativeToSecondEnumMember)
            return true; //The DataTrigger will collapse the ComboBoxItem because of the Value="true"
        return false;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

The EnumerationExtension is here

Community
  • 1
  • 1
ZwoRmi
  • 1,093
  • 11
  • 30
0

That's the solution I've found:

    public static IList ToList(this Type type, Enum[] valuesToSkip)
    {
        ArrayList enumList = new ArrayList();
        Array enumValues = Enum.GetValues(type);

        foreach (Enum value in enumValues)
        {
            if (valuesToSkip.Contains(value))
                continue;

            // in this solution I had a method to get the description from the Enum
            // but you can avoid this part and populate your list with the enum values
            //enumList.Add(new KeyValuePair<Enum, string>(value, GetEnumDescription(value)));
              enumList.Add(value);
        }

        return list;
    }

I've wrote my own ToList method that gives back the enum excluding the ones that I pass through valuesToSkip. I populate the combobox in this way:

comboBox.DataSource = EnumExtension.ToList(typeof(yourEnum), new Enum[] { yourEnum.ValueToSkip });

Mark
  • 1
  • 1