Wpf Binding a Listbox to an Enum, Displaying the Description Attribute

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:

Sample Image

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



Leave a reply



Submit