How do I have an enum bound combobox with custom string formatting for enum values?
You could write an TypeConverter that reads specified attributes to look them up in your resources. Thus you would get multi-language support for display names without much hassle.
Look into the TypeConverter's ConvertFrom/ConvertTo methods, and use reflection to read attributes on your enum fields.
Bind Combobox with Enum Description
Try this:
cbTipos.DisplayMember = "Description";
cbTipos.ValueMember = "Value";
cbTipos.DataSource = Enum.GetValues(typeof(TiposTrabajo))
.Cast<Enum>()
.Select(value => new
{
(Attribute.GetCustomAttribute(value.GetType().GetField(value.ToString()), typeof(DescriptionAttribute)) as DescriptionAttribute).Description,
value
})
.OrderBy(item => item.value)
.ToList();
In order for this to work, all the values must have a description or you'll get a NullReference Exception. Hope that helps.
How do I bind an enum to a ComboBox and hide a certain value in C#
The GetValues
method will return all of these constants as an array. Instead create a custom list.
ThisComboBOx.ItemsSource = new List<BlueOrRed> {BlueOrRed.Blue, BlueOrRed.Red};
If you do not want to create the list yourself, you can also exclude the None
constant using Linq.
ThisComboBOx.ItemsSource = ((BlueOrRed[]) Enum.GetValues(typeof(BlueOrRed))).Except(new[] { BlueOrRed.None });
Friendly-Format Enum for use in ComboBoxes, CheckedListBoxes, etc
The basis of my solution is a struct
that can wrap Enum
values and override ToString()
.
Enter the EnumWrapper
:
public struct EnumWrapper
{
private readonly Enum e;
public EnumWrapper(Enum e) {
this.e = e;
}
public static implicit operator Enum(EnumWrapper wrapper) {
return wrapper.e;
}
public static explicit operator EnumWrapper(Enum e) {
return new EnumWrapper(e);
}
public override string ToString() {
return e.ToStringFriendly();
}
}
The method ToStringFriendly()
is defined as an extension method on Enum
:
using System.Text.RegularExpressions;
public static class _Extensions
{
public static string ToStringFriendly(this Enum e)
{
string s = e.ToString();
// enforce a charset: letters, numbers, and underscores
s = Regex.Replace(s, "[^A-Za-z0-9_]", "");
// separate numbers from letters
s = Regex.Replace(s, "([a-zA-Z])([0-9])", "$1 $2");
// separate letters from numbers
s = Regex.Replace(s, "([0-9])([a-zA-Z])", "$1 $2");
// space lowercases before uppercase (word boundary)
s = Regex.Replace(s, "([a-z])([A-Z])", "$1 $2");
// see that the nice pretty capitalized words are spaced left
s = Regex.Replace(s, "(?!^)([^ _])([A-Z][a-z]+)", "$1 $2");
// replace double underscores with colon-space delimiter
s = Regex.Replace(s, "__", ": ");
// finally replace single underscores with hyphens
s = Regex.Replace(s, "_", "-");
return s;
}
}
Now, to add any Enum
value to a ComboBox, for example,
comboBox.Items.Add((EnumWrapper)MyEnum.SomeValue);
And to get it back out (after null-testing, of course):
MyEnum myValue = (MyEnum)(Enum)(EnumWrapper)comboBox.SelectedItem;
And that's it. Now what are the pros and cons of this approach?
The Pros:
- You can pass the enum values (almost) directly in and out of your controls. Just cast back and forth to/from
EnumWrapper
. - This works nicely for all Pascal-cased
Enum
values with a few special cases. One example might be a value calledMyValue__DescriptionOf123_ENUMValue
which would come out ofToStringFriendly()
as "My Value: Description of 123-ENUM Value". - The mechanics (a
struct
and an extension method) can be written once and tucked out of the way. There is no additional coding in eachEnum
. This means it works nicely as above for enums you didn't write, assuming Pascal case, which in .NET is a good assumption. Note: This is why I'd say this answer, however elegant, is not a better solution than mine, as it requires that you can add aTypeConverter
attribute to eachEnum
.
The Cons:
- This doesn't support custom descriptions or multiple languages. For example a value like
Encoding.EBCDIC
will always come through as "EBCDIC" and does not allow you to manually type "Extended Binary Coded Decimal Interchange Code", much less other languages.
Future Work
One could add custom descriptions and multi-language support by changing ToStringFriendly()
to do a language-lookup for that value before de-Pascal-casing.
For more Regex fun, see this thread.
ComboBox bound to an enum type's values while also having a blank entry?
(Please see my edit to the question where I clarified that I don't want to bind to a collection of strings).
After more fiddling, the following monstrosity seems to work. combo1.SelectedItem is of type object and will either be a DBNull or a (boxed?) enum value. Is this code advisable?
combo1.DataSource = (new object[] { DBNull.Value }
.Concat(Enum.GetValues(refToAnEnumType)
.Cast<object>())).ToList()
Edit: I see Adam and Andrew's methods could easily be adapted to do the same thing. Thanks guys!
Custom define enum names for combobox
use the DescriptionAttribute
and an appropriate extension method to read it out.
public enum BaudRate
{
[Description("115200 kb")]
BR115200 = 7,
[Description("19200 kb")]
BR19200 = 4,
[Description("230400 kb")]
BR230400 = 8,
[Description("2400 kb")]
BR2400 = 1,
[Description("115200 kb")]
BR38400 = 5,
[Description("4800 kb")]
BR4800 = 2,
[Description("57600 kb")]
BR57600 = 6,
[Description("9600 kb")]
BR9600 = 3
}
The extension method:
public static class EnumExtension
{
/// <summary>
/// Gets the string of an DescriptionAttribute of an Enum.
/// </summary>
/// <param name="value">The Enum value for which the description is needed.</param>
/// <returns>If a DescriptionAttribute is set it return the content of it.
/// Otherwise just the raw name as string.</returns>
public static string Description(this Enum value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
DescriptionAttribute[] attributes =
(DescriptionAttribute[])
fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
description = attributes[0].Description;
}
return description;
}
/// <summary>
/// Creates an List with all keys and values of a given Enum class
/// </summary>
/// <typeparam name="T">Must be derived from class Enum!</typeparam>
/// <returns>A list of KeyValuePair<Enum, string> with all available
/// names and values of the given Enum.</returns>
public static IList<KeyValuePair<Enum, string>> ToList<T>() where T : struct
{
var type = typeof(T);
if (!type.IsEnum)
{
throw new ArgumentException("T must be an enum");
}
return (IList<KeyValuePair<Enum, string>>)
Enum.GetValues(type)
.OfType<Enum>()
.Select(e => new KeyValuePair<Enum, string>(e, e.Description()))
.ToArray();
}
public static T GetValueFromDescription<T>(string description) where T : struct
{
var type = typeof(T);
if(!type.IsEnum)
{
throw new ArgumentException("T must be an enum");
}
foreach(var field in type.GetFields())
{
var attribute = Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if(attribute != null)
{
if(attribute.Description == description)
{
return (T)field.GetValue(null);
}
}
else
{
if(field.Name == description)
{
return (T)field.GetValue(null);
}
}
}
throw new ArgumentOutOfRangeException("description");
// or return default(T);
}
}
At the and you can simply apply this to your combo box by calling:
var list = EnumExtension.ToList<BaudRate>();
myComboBox.DataSource = list;
myComboBox.ValueMember = "Key";
myComboBox.DisplayMember = "Value";
Setting ComboBox DataSource to an enum and binding SelectedValue
Option 1
To use SelectedValue
for data binding, Create a calss DataItem:
public class DataItem
{
public MyEnum Value { get; set; }
public string Text { get; set; }
}
Then use this code for data binding:
this.comboBox1.DataSource = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()
.Select(x => new DataItem() { Value = x, Text = x.ToString() })
.ToList();
this.comboBox1.ValueMember = "Value";
this.comboBox1.DisplayMember = "Text";
this.comboBox1.DataBindings.Add(
new Binding("SelectedValue", yourObjectToBind, "PropertyOfYourObject"));
You can make generic DataItem<T>
containing public T Value { get; set; }
to make it more reusable in your project.
Option 2
To Use SelectedItem
for data binding:
this.comboBox1.DataSource = Enum.GetValues(typeof(MyEnum));
this.comboBox1.DataBindings.Add(
new Binding("SelectedItem", yourObjectToBind, "PropertyOfYourObject"));
Option 3
To use SelectedValue
for data binding as another option suggested by Ivan Stoev, you can perform data to binding this way:
this.comboBox1.DataSource = Enum.GetValues(typeof(MyEnum));
this.comboBox1.DataBindings.Add(
new Binding("SelectedValue", yourObjectToBind, "PropertyOfYourObject",
true, DataSourceUpdateMode.OnPropertyChanged));
Edit by Jannik:
The basic generic approach of option 1 would look like this:
public class ComboBoxItemWrapper<T>
{
public T Value { get; set; }
public string Text { get; set; }
}
Related Topics
C# Optional Parameters on Overridden Methods
Linq to Entities Only Supports Casting Edm Primitive or Enumeration Types with Ientity Interface
How to Draw Directly on the Windows Desktop, C#
Recursive Hierarchy - Recursive Query Using Linq
Is There a Lower Bound Function on a Sortedlist<K ,V>
Displaying Arabic Characters in C# Console Application
What Are the Benefits of Using C# VS F# or F# VS C#
Best Practice to Make a Multi Language Application in C#/Winforms
What Is the Correct Performance Counter to Get CPU and Memory Usage of a Process
Transactionscope VS Transaction in Linq to SQL
HTML Agility Pack Strip Tags Not in Whitelist
How to Determine If a File Is Binary or Text in C#