The example here is applied to a ComboBox, but will work all the same for any Enum Binding. It will however do exactly what you want: Hide Enum values that don't have a Description. This also provides an easy Binding method. (as well as sorting them by Index though, can be easily changed in the function "SortEnumValuesByIndex()")
Origin:
This anwser is based on the original work of Brian Lagunas' EnumBindingSourceExtension + EnumDescriptionTypeConverter.
I have made modifications for it to better suit my needs.
What I changed:
Extended the Enum class with a boolean function that checks if the EnumValue has the [Description] attribute or not
public static bool HasDescriptionAttribute(this Enum value)
{
var attribute = value.GetType().GetField(value.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.FirstOrDefault();
return (attribute != null);
}
Modified Brian's "ConvertTo()" function in "EnumDescriptionTypeConverter" to return "null" in case the [Description] attribute was not applied
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
...
// Original: return ((attributes.Length > 0) && (!String.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : value.ToString();
return ((attributes.Length > 0) && (!String.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : null;
}
Modified Brian's "ProvideValue()" function in "EnumBindingSourceExtension" by editing it's return value by calling my own function
ProvideValue(IServiceProvider serviceProvider)
{
...
// Original: return enumValues
return SortEnumValuesByIndex(enumValues);
...
// Original: return tempArray
return SortEnumValuesByIndex(tempArray);
}
And adding my function to Sort the enum by Index (Original code had problems when going across Projects ..), and Strip out any Values that don't have the [Description] attribute:
private object SortEnumValuesByIndex(Array enumValues)
{
var values = enumValues.Cast<Enum>().ToList();
var indexed = new Dictionary<int, Enum>();
foreach (var value in values)
{
int index = (int)Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()));
indexed.Add(index, value);
}
return indexed.OrderBy(x => x.Key).Select(x => x.Value).Where(x => x.HasDescriptionAttribute()).Cast<Enum>();
}
This example has been applied to ComboBoxes:
Note: Failures in Uploading images to the Server, so i added a URL to the images in question
<ComboBox x:Name="ConversionPreset_ComboBox" Grid.Row="4" Grid.Column="1" Margin="5,5,5,5" ItemsSource="{objects:EnumBindingSource EnumType={x:Type enums:ConversionPreset}}" SelectedIndex="2" SelectionChanged="ConversionPreset_ComboBox_SelectionChanged" />
<ComboBox x:Name="OutputType_ComboBox" Grid.Row="4" Grid.Column="2" Margin="5,5,5,5" ItemsSource="{objects:EnumBindingSource EnumType={x:Type enums:Output}}" SelectedIndex="1" SelectionChanged="OutputType_ComboBox_SelectionChanged" />
With code behind:
private Enumeration.Output Output { get; set; }
private void OutputType_ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
=> Output = (Enumeration.Output)OutputType_ComboBox.SelectedItem;
private Enumeration.ConversionPreset ConversionPreset { get; set; }
private void ConversionPreset_ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
=> ConversionPreset = (Enumeration.ConversionPreset)ConversionPreset_ComboBox.SelectedItem;
The "ConversionPreset" Enum and a Picture of the ComboBox Rendered
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum ConversionPreset
{
[Description("Very Slow (Smaller File Size)")]
VerySlow = -2,
[Description("Slow (Smaller File Size)")]
Slow = -1,
[Description("Medium (Balanced File Size)")]
Medium = 0,
[Description("Fast (Bigger File Size)")]
Fast = 1,
[Description("Very Fast (Bigger File Size)")]
VeryFast = 2,
[Description("Ultra Fast (Biggest File Size)")]
UltraFast = 3
}
The "Output" Enum and a Picture of the ComboBox Rendered
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum Output
{
// This will be hidden in the Output
None = -1,
[Description("Video")]
Video = 0,
[Description("Audio")]
Audio = 1
}
Update
The code above is intended to Bind Enums and Manage their Output without the need for Code Behind to manually fill the ComboBoxes.
I just now read that you want to approach it from in Code, having more control over the ComboBox Content.
This should get you on your way just fine. Just create a class and paste this content into it:
using System;
using System.ComponentModel;
namespace Attributes
{
public class Hidden : Attribute { }
public class StartingDescription : DescriptionAttribute
{
public StartingDescription(string description) : base(description) { }
}
public class FinishedDescription : DescriptionAttribute
{
public FinishedDescription(string description) : base(description) { }
}
}
namespace Enumerations
{
using Attributes;
public enum Order
{
None = -1,
[Description("Get"),
StartingDescription("Getting"),
FinishedDescription("Got")]
Get = 0,
[Description("Initialize"),
StartingDescription("Initializing"),
FinishedDescription("Initialized")]
Initialize = 1,
[Description("Download"),
StartingDescription("Downloading"),
FinishedDescription("Downloaded")]
Download = 2
}
public enum Output
{
[Hidden]
None = -1,
[Description("Video")]
Video = 33,
[Description("Audio")]
Audio = 44
}
}
namespace Classes
{
using System.Linq;
public static class Extensions
{
public static string Format(this string value, params object?[] args)
=> String.Format(value, args);
public static bool HasAttribute(this Enum enumValue, Type attributeType)
{
var result = enumValue.GetType().GetField(enumValue.ToString())
.GetCustomAttributes(attributeType, false)
.FirstOrDefault();
return (result != null);
}
public static int GetIndex(this Enum enumValue)
=> (int)Convert.ChangeType(enumValue, Enum.GetUnderlyingType(enumValue.GetType()));
public static string GetDescription(this Enum enumValue, Type attributetype = null)
{
if (attributetype == null)
attributetype = typeof(DescriptionAttribute);
var field = enumValue.GetType().GetField(enumValue.ToString());
var attributes = Attribute.GetCustomAttributes(field, false).Where(x => x.GetType().Equals(attributetype)).Cast<DescriptionAttribute>();
var value = attributes.FirstOrDefault();
if (value != null)
return value.Description;
return enumValue.ToString();
}
}
}
namespace ExecutionNamespace
{
using System.Collections.Generic;
using System.Linq;
using Classes;
using Attributes;
using System.Diagnostics;
internal class Example
{
public static SortedList<int, string> GetEnumByIndex(Type enumType, Type outputType = null)
{
if (enumType == null)
throw new ArgumentNullException("enumType was not Definied");
var enumValues = Enum.GetValues(enumType).Cast<Enum>().ToList();
if ((enumValues == null) || (enumValues.Count == 0))
throw new ArgumentNullException("Could not find any Enumeration Values");
if (outputType == null)
outputType = typeof(DescriptionAttribute);
var indexed = new SortedList<int, string>();
foreach (var value in enumValues)
if (!value.HasAttribute(typeof(Hidden)))
indexed.Add(value.GetIndex(), value.GetDescription(outputType));
return indexed;
}
public static SortedList<string, string> GetEnumByValue(Type enumType, Type outputType = null)
{
if (enumType == null)
throw new ArgumentNullException("enumType was not Definied");
var enumValues = Enum.GetValues(enumType).Cast<Enum>().ToList();
if ((enumValues == null) || (enumValues.Count == 0))
throw new ArgumentNullException("Could not find any Enumeration Values");
if (outputType == null)
outputType = typeof(DescriptionAttribute);
var indexed = new SortedList<string, string>();
foreach (var value in enumValues)
if (!value.HasAttribute(typeof(Hidden)))
indexed.Add(value.ToString(), value.GetDescription(outputType));
return indexed;
}
public static void Run()
{
Type type = null;
type = typeof(Enumerations.Order);
Debug.WriteLine("{0} by Index".Format(type.ToString()));
foreach (var valuePair in GetEnumByIndex(type, typeof(StartingDescription)))
Debug.WriteLine("Index:{1} & Description:{2} ".Format(type.ToString(), valuePair.Key, valuePair.Value));
Debug.WriteLine("");
type = typeof(Enumerations.Order);
Debug.WriteLine("{0} by Value".Format(type.ToString()));
foreach (var valuePair in GetEnumByValue(type, typeof(StartingDescription)))
Debug.WriteLine("Value:{1} & Description:{2} ".Format(type.ToString(), valuePair.Key, valuePair.Value));
Debug.WriteLine("");
type = typeof(Enumerations.Output);
Debug.WriteLine("{0} by Index".Format(type.ToString()));
foreach (var valuePair in GetEnumByIndex(type))
Debug.WriteLine("Index:{1} & Description:{2} ".Format(type.ToString(), valuePair.Key, valuePair.Value));
Debug.WriteLine("");
type = typeof(Enumerations.Output);
Debug.WriteLine("{0} by Value".Format(type.ToString()));
foreach (var valuePair in GetEnumByValue(type))
Debug.WriteLine("Value:{1} & Description:{2} ".Format(type.ToString(), valuePair.Key, valuePair.Value));
Debug.WriteLine("");
}
}
}
Then just do this on like a Button Click, and watch the Output Window:
private void Button_Click(object sender, RoutedEventArgs e)
{
ExecutionNamespace.Example.Run();
}
It will bring you this output:
Enumerations.Order by Index
Index:-1 & Description:None
Index:0 & Description:Getting
Index:1 & Description:Initializing
Index:2 & Description:Downloading
Enumerations.Order by Value
Value:Download & Description:Downloading
Value:Get & Description:Getting
Value:Initialize & Description:Initializing
Value:None & Description:None
Enumerations.Output by Index
Index:33 & Description:Video
Index:44 & Description:Audio
Enumerations.Output by Value
Value:Audio & Description:Audio
Value:Video & Description:Video