WPF Binding a ListBox to an enum, displaying the Description Attribute
Yes, it is possible. This will do it. Say we have the enum
public enum MyEnum
{
[Description("MyEnum1 Description")]
MyEnum1,
[Description("MyEnum2 Description")]
MyEnum2,
[Description("MyEnum3 Description")]
MyEnum3
}
Then we can use the ObjectDataProvider as
xmlns:MyEnumerations="clr-namespace:MyEnumerations"
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="MyEnumValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="MyEnumerations:MyEnum" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
And for the ListBox we set the ItemsSource to MyEnumValues and apply an ItemTemplate with a Converter.
<ListBox Name="c_myListBox" SelectedIndex="0" Margin="8"
ItemsSource="{Binding Source={StaticResource MyEnumValues}}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in the converter we get the description and return it
public class EnumDescriptionConverter : IValueConverter
{
private string GetEnumDescription(Enum enumObj)
{
FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());
object[] attribArray = fieldInfo.GetCustomAttributes(false);
if (attribArray.Length == 0)
{
return enumObj.ToString();
}
else
{
DescriptionAttribute attrib = attribArray[0] as DescriptionAttribute;
return attrib.Description;
}
}
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Enum myEnum = (Enum)value;
string description = GetEnumDescription(myEnum);
return description;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.Empty;
}
}
The GetEnumDescription method should probably go somewhere else but you get the idea :)
Check GetEnumDescription as extension method.
Binding to display name attribute of enum in xaml
Create a class implementing the System.Windows.Data.IValueConverter
interface and specify it as the binding's converter. Optionally, for easier usage, you can create a "provider" class implementing System.Windows.Markup.MarkupExtension
(actually you can do both with just one class). Your end result could resemble this example:
public class MyConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((Enum)value).GetAttributeOfType<DisplayAttribute>().Name;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
And then in XAML:
<Label Content="{Binding CurrentViewModel.ViewMode, Converter={local:MyConverter}}" Grid.Column="2" VerticalContentAlignment="Bottom" Height="43" HorizontalContentAlignment="Right"/>
How to set Enum to ItemsSource WPF
From a production app
Got this off SO a while ago and cannot find the source
Bind the DisplayMememberPath to Value
public static Dictionary<T, string> EnumToDictionary<T>()
where T : struct
{
Type enumType = typeof(T);
// Can't use generic type constraints on value types,
// so have to do check like this
if (enumType.BaseType != typeof(Enum))
throw new ArgumentException("T must be of type System.Enum");
Dictionary<T, string> enumDL = new Dictionary<T, string>();
foreach (T val in Enum.GetValues(enumType))
{
enumDL.Add(val, val.ToString());
}
return enumDL;
}
GetDescription Method
For those who want to know how to read the description attribute value. The following can easily be converted to use enum
or into an extenstion. I found this implementation is more flexible.
Using this method, replace val.ToString()
with GetDescription(val)
.
/// <summary>
/// Returns the value of the 'Description' attribute; otherwise, returns null.
/// </summary>
public static string GetDescription(object value)
{
string sResult = null;
FieldInfo oFieldInfo = value.GetType().GetField(value.ToString());
if (oFieldInfo != null)
{
object[] oCustomAttributes = oFieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
if ((oCustomAttributes != null) && (oCustomAttributes.Length > 0))
{
sResult = ((DescriptionAttribute)oCustomAttributes[0]).Description;
}
}
return sResult;
}
WPF C# representing List of enums as ListBox and binding the value to combobox doesn't work two way
As far as I can tell you are binding to a list of enums (which happens to be in an observable collections), ok.
When an enum is added one sees it on the screen because the binding to the observable collection is smart enough subscribe to the change notification from the observable collection.
But it seems you want the enum in the list to change to a different enum via direct combobox binding; that is not possible because the observable collection is not being considered.
Why?
In a sense you are indirectly asking the observable collection to delete the item, then add a new item in its place and then send a messages about each of the changes. That is not possible with just a binding to the reference
of the selected item of the list.
What to do?
It would be better to simply use the list box, remove that DataTemplate
you have and create buttons below it Add
, Delete
and Replace
, possibly with a side drop down combobox as a selection of the enums. then wire up the buttons in the code behind to work with the observable collection directly by removing the selected and replacing it if necessary.
Description attribute on enum values in WPF DataGrid with AutoGenerateColumns
You are working too hard to do this, since the enum is not a changeable item, during the VM construction enumerate the enum and extract the descriptions into a list then bind the control to that list.
Example
Take our enums
public enum TheNums
{
[Description("One")]
Alpha,
[Description("Two")]
Beta,
[Description("Three")]
Gamma
}
The extension method to extract descriptions
public static class AttributeExtension
{
/// <summary>If an attribute on an enumeration exists, this will return that
/// information</summary>
/// <param name="value">The object which has the attribute.</param>
/// <returns>The description string of the attribute or string.empty</returns>
public static string GetAttributeDescription(this object value)
{
string retVal = string.Empty;
try
{
retVal = value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.OfType<DescriptionAttribute>()
.First()
.Description;
}
catch (NullReferenceException)
{
//Occurs when we attempt to get description of an enum value that does not exist
}
finally
{
if (string.IsNullOrEmpty(retVal))
retVal = "Unknown";
}
return retVal;
}
}
The creation of our list of strings List<string> EnumDescriptions { get; set; }
:
EnumDescriptions = new List<string>()
{
TheNums.Alpha.GetAttributeDescription(),
TheNums.Beta.GetAttributeDescription(),
TheNums.Gamma.GetAttributeDescription()
};
For the sake of simplicity I will add it to the page's datacontext so I don't have to path into the list named EnumDescriptions
.
public List<string> EnumDescriptions { get; set; }
public MainWindow()
{
InitializeComponent();
EnumDescriptions = new List<string>()
{
TheNums.Alpha.GetAttributeDescription(),
TheNums.Beta.GetAttributeDescription(),
TheNums.Gamma.GetAttributeDescription()
};
DataContext = EnumDescriptions;
}
Then on my page I will bind to it directly, since Listbox inheirits the page's datacontext which is the EnumDescriptions
.
<ListBox ItemsSource="{Binding}" Width="100" Height="200"/>
The result is:
Note that in an MVVM implementation, most likely the whole VM instance would be the page's data context, so the binding needs to know the property name (its binding path
) off of the data context/ the VM instance, so use Binding EnumDescriptions
or Binding Path=EnumDescriptions
.
Show Enum Description Instead of Name
If you keep this ItemsSource
you will have to define a custom ItemTemplate
as the DisplayMemberPath
is just a path via which you will not be able to retrieve the description.
As for what the template should look like: You can bind a TextBlock
to the enum value (the current DataContext
) and pipe that through a ValueConverter
using Binding.Converter
. The code would just be some reflection to retrieve the Description
(GetType
, GetCustomAttributes
etc.)
Alternatives are a custom method that return a usable collection right away (and is used in the ObjectDataProvider
) or a custom markup extension which does the same thing.
Method example if we are talking about a ComponentModel.DescriptionAttribute
:
public static class EnumUtility
{
// Might want to return a named type, this is a lazy example (which does work though)
public static object[] GetValuesAndDescriptions(Type enumType)
{
var values = Enum.GetValues(enumType).Cast<object>();
var valuesAndDescriptions = from value in values
select new
{
Value = value,
Description = value.GetType()
.GetMember(value.ToString())[0]
.GetCustomAttributes(true)
.OfType<DescriptionAttribute>()
.First()
.Description
};
return valuesAndDescriptions.ToArray();
}
}
<ObjectDataProvider x:Key="Data" MethodName="GetValuesAndDescriptions"
ObjectType="local:EnumUtility">
<ObjectDataProvider.MethodParameters>
<x:TypeExtension TypeName="local:TestEnum" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ListBox ItemsSource="{Binding Source={StaticResource Data}}"
DisplayMemberPath="Description"
SelectedValuePath="Value"/>
How to bind an enum to a combobox control in WPF?
You can do it from code by placing the following code in Window Loaded
event handler, for example:
yourComboBox.ItemsSource = Enum.GetValues(typeof(EffectStyle)).Cast<EffectStyle>();
If you need to bind it in XAML you need to use ObjectDataProvider
to create object available as binding source:
<Window x:Class="YourNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects">
<Window.Resources>
<ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues"
ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="StyleAlias:EffectStyle"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}"
SelectedItem="{Binding Path=CurrentEffectStyle}" />
</Grid>
</Window>
Draw attention on the next code:
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects"
Guide how to map namespace and assembly you can read on MSDN.
Set the Enum display value C#
You'll want to use the Description attribute from System.ComponentModel.DataAnnotations
The answers to this question have the code you need:
Enum ToString with user friendly strings
Related Topics
What Advantages of Extension Methods Have You Found
What Is the Algorithm to Convert an Excel Column Letter into Its Number
Routing: the Current Request for Action [...] Is Ambiguous Between the Following Action Methods
Loading Multiple Versions of the Same Assembly
Is Inaccessible Due to Its Protection Level
C# Naming Convention for Constants
Compare Equality Between Two Objects in Nunit
Export Datatable to Excel File
Linq to SQL Multiple Tables Left Outer Join
Scope of Static Variable in Multi-User ASP.NET Web Application
How to Add Claims in ASP.NET Identity
How to Run a .Net Console Application in the Background
Matching Strings with Wildcard
How to Protect Resources That May Be Used in a Multi-Threaded or Async Environment
Jtoken: Get Raw/Original JSON Value
Why Are C# 4 Optional Parameters Defined on Interface Not Enforced on Implementing Class
Why Do Local Variables Require Initialization, But Fields Do Not