I have a ComboBox listing an Enum.
enum StatusEnum {
Open = 1, Closed = 2, InProgress = 3
}
<ComboBox ItemsSource="{Binding StatusList}"
SelectedItem="{Binding SelectedStatus}" />
I want to display localized names for the enum values in English
Open
Closed
In Progress
but also in German (and other languages in the future)
Offen
Geschlossen
In Arbeit
In my ViewModel using
public IEnumerable<StatusEnum> StatusList
{
get
{
return Enum.GetValues(typeof(StatusEnum)).Cast<StatusEnum>();
}
}
only gets me the names of the enum in the code and not the translated ones.
I have general localization in place and can access them using i.e.
Resources.Strings.InProgress
which gets me the translation for the current language.
How can I bind the localization automatically?
It's an example of the simple Enum to translated string converter.
public sealed class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{ return null; }
return Resources.ResourceManager.GetString(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string str = (string)value;
foreach (object enumValue in Enum.GetValues(targetType))
{
if (str == Resources.ResourceManager.GetString(enumValue.ToString()))
{ return enumValue; }
}
throw new ArgumentException(null, "value");
}
}
Also you need a MarkupExtension which will provide values:
public sealed class EnumerateExtension : MarkupExtension
{
public Type Type { get; set; }
public EnumerateExtension(Type type)
{
this.Type = type;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
string[] names = Enum.GetNames(Type);
string[] values = new string[names.Length];
for (int i = 0; i < names.Length; i++)
{ values[i] = Resources.ResourceManager.GetString(names[i]); }
return values;
}
}
Usage:
<ComboBox ItemsSource="{local:Enumerate {x:Type local:StatusEnum}}"
SelectedItem="{Binding SelectedStatus, Converter={StaticResource EnumToStringConverter}}" />
EDIT: You can make a more complex value converter and markup extension. The EnumToStringConverter can use DescriptionAttribute's to get the translated strings. And the EnumerateExtension can use TypeConverter.GetStandardValues() and a converter. This allows to get standard values of the specified type (not only Enums) and convert them to strings or something another depending on the converter.
Example:
<ComboBox ItemsSource="{local:Enumerate {x:Type sg:CultureInfo}, Converter={StaticResource CultureToNameConverter}}"
SelectedItem="{Binding SelectedCulture, Converter={StaticResource CultureToNameConverter}}" />
EDIT: The more complex solution described above is published on GitHub now.
You can do using a Attribute for the enum and writing an extension method for the enum. Refer the below code.
<ComboBox Width="200" Height="25" ItemsSource="{Binding ComboSource}"
DisplayMemberPath="Value"
SelectedValuePath="Key"/>
public class MainViewModel
{
public List<KeyValuePair<Status, string>> ComboSource { get; set; }
public MainViewModel()
{
ComboSource = new List<KeyValuePair<Status, string>>();
Status st=Status.Open;
ComboSource = re.GetValuesForComboBox<Status>();
}
}
public enum Status
{
[Description("Open")]
Open,
[Description("Closed")]
Closed,
[Description("InProgress")]
InProgress
}
public static class ExtensionMethods
{
public static List<KeyValuePair<T, string>> GetValuesForComboBox<T>(this Enum theEnum)
{
List<KeyValuePair<T, string>> _comboBoxItemSource = null;
if (_comboBoxItemSource == null)
{
_comboBoxItemSource = new List<KeyValuePair<T, string>>();
foreach (T level in Enum.GetValues(typeof(T)))
{
string Description = string.Empty;
FieldInfo fieldInfo = level.GetType().GetField(level.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
Description = GetDataFromResourceFile(attributes.FirstOrDefault().Description);
}
KeyValuePair<T, string> TypeKeyValue = new KeyValuePair<T, string>(level, Description);
_comboBoxItemSource.Add(TypeKeyValue);
}
}
return _comboBoxItemSource;
}
public static string GetDataFromResourceFile(string key)
{
//Do you logic to get from resource file based on key for a language.
}
}
I have already posted a similar thing in SO Is it possible to databind to a Enum, and show user-friendly values?
You can't, out of the box.
But you can create an ObservableList<KeyValuePair<StatusEnum, string>> property and fill it with your enum/localized text and then bind it to your ComboBox.
As for the string itself:
var localizedText = (string)Application.Current.FindResource("YourEnumStringName");
Getting the Enum string representation with Enum.GetName/Enum.GetNames methods.
Related
I have the following consts I use to check the database values f.e.:
public static class ConnectionConst
{
public const int NotConnected = 0;
public const int Connected = 1;
public const int Unknown = 2;
// ...
}
Now I don't want to show the integer values in the datagrid but the values of the const properties. F.e. 'Connected' instead of '1'.
why not using an enum:
public enum ConnectionConst
{
NotConnected = 0,
Connected = 1,
Unknown = 2
}
You can have a variable of this type:
ConnectionConst connectionState = ConnectionConst.Unknown;
and in the DataGridView the value "Unknown" should appear
EDIT:
if you are already using C# 6 or higher you could also use nameof in your example with the static class:
string value = nameof(ConnectionConst.Unknown);
If you want to display name with spaces like Not Connected instead of NotConnected you can try to use this:
public enum ConnectionConst
{
[Description("Not Connected")]
NotConnected = 0,
[Description("Connected")]
Connected = 1,
[Description("Unknown")]
Unknown = 2
}
public static string DisplayEnumName(Enum value)
{
var name = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[])name.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0) {
return attributes(0).Description;
} else {
return value.ToString();
}
}
Then just use this:
var name = DisplayEnumName(ConnectionConst.NotConnected);
In addition to what XardasLord and DavidG suggested, I'd put that enum description getting code into an IValueConverter.
public class EnumDescriptionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return "";
string stringRepresentation = value.ToString();
Type T = value.GetType();
if (!T.IsEnum) return stringRepresentation;
MemberInfo[] enumMembers = T.GetMember(stringRepresentation);
if (enumMembers.Length <= 0) return stringRepresentation;
DescriptionAttribute[] memberAttributes = enumMembers[0].GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
if (memberAttributes == null) return stringRepresentation;
if (memberAttributes.Length <= 0) return stringRepresentation;
return memberAttributes[0].Description;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Or Binding.DoNothing, or throw an Exception, whichever you prefer
return DependencyProperty.UnsetValue;
}
}
And use it in the Binding of your Column:
<DataGrid>
<DataGrid.Resources>
<local:EnumDescriptionConverter x:Key="DescriptionConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=YourMemberNameHere, Converter={StaticResource DescriptionConverter}}"/>
</DataGrid.Columns>
</DataGrid>
I'm using columns autogeneration feature of WPF DataGrid control. One of it's columns is a select column - based on some enum.
The enum looks like this:
public enum MyTypes {
Integer = 1,
Float = 2
IntegerArray = 3,
FloatArray = 4
}
I'd like to show the array types not as IntegerArray, FloatArray, but as Integer[],
Float[] in the autogenerated dropdown list.
In other words - cell will contain a dropdown list with values Integer, Float, IntegerArray, FloatArray, and I want them to be Integer, Float, Integer[], Float[]. Obviously I can't change IntegerArray to Integer[] inside my MyTypes declaration.
How do I do that?
EDIT:
Pushpray's answer below works only partially - I get enum fields description (so instead of having FloatArr in the ComboBox I'm getting Float[], but when the column holding theComboBox looses focus, then I get NullReferenceException.
here is how I offer to solve your issue
result
we will start by defining the desired values as Description attribute to the enum values
public enum MyTypes
{
Integer = 1,
Float = 2,
[Description("Integer[]")]
IntegerArray = 3,
[Description("Float[]")]
FloatArray = 4
}
then create a class with a method to enumerate the list from the enum type that will take Description attribute into account if applied
namespace CSharpWPF
{
public class EnumHelper
{
public static IEnumerable<string> GetEnumDescriptions(Type enumType)
{
foreach (var item in Enum.GetNames(enumType))
{
FieldInfo fi = enumType.GetField(item);
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
yield return attributes[0].Description;
else
yield return item;
}
}
}
}
finally use ObjectDataProvider to use the enumerator method GetEnumDescriptions in the class EnumHelper, and use the same as the source for the DataGridComboBoxColumn's ItemsSource
sample xaml
<DataGrid xmlns:l="clr-namespace:CSharpWPF">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="EnumValues" >
<DataGridComboBoxColumn.ItemsSource>
<Binding>
<Binding.Source>
<ObjectDataProvider MethodName="GetEnumDescriptions"
ObjectType="{x:Type l:EnumHelper}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="l:MyTypes" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Binding.Source>
</Binding>
</DataGridComboBoxColumn.ItemsSource>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
Using with Auto generating columns
<DataGrid x:Name="dGrid"
AutoGenerateColumns="True"
AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn"
xmlns:l="clr-namespace:CSharpWPF">
<DataGrid.Resources>
<l:EnumHelper x:Key="EnumHelper" />
<ObjectDataProvider x:Key="EnumValues"
MethodName="GetEnumDescriptions"
ObjectType="{x:Type l:EnumHelper}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="l:MyTypes" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<DataTemplate x:Key="MyTypesCellTemplate">
<TextBlock Text="{Binding EnumValue, Converter={StaticResource EnumHelper}}"/>
</DataTemplate>
<DataTemplate x:Key="MyTypesCellEditingTemplate">
<ComboBox SelectedItem="{Binding EnumValue, Converter={StaticResource EnumHelper}}"
ItemsSource="{Binding Source={StaticResource EnumValues}}" />
</DataTemplate>
</DataGrid.Resources>
</DataGrid>
event handler
private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyType == typeof(MyTypes))
{
DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();
templateColumn.Header = e.Column.Header;
templateColumn.CellTemplate = (DataTemplate)dGrid.Resources["MyTypesCellTemplate"];
templateColumn.CellEditingTemplate = (DataTemplate)dGrid.Resources["MyTypesCellEditingTemplate"];
e.Column = templateColumn;
}
}
EnumHelper class
public class EnumHelper : IValueConverter
{
public static IEnumerable<string> GetEnumDescriptions(Type enumType)
{
foreach (var item in Enum.GetNames(enumType))
{
FieldInfo fi = enumType.GetField(item);
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
yield return attributes[0].Description;
else
yield return item;
}
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return string.Empty;
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return MyTypes.Float;
Type enumType = typeof(MyTypes);
foreach (var item in Enum.GetNames(enumType))
{
FieldInfo fi = enumType.GetField(item);
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0 && string.Equals(attributes[0].Description, value))
return Enum.Parse(enumType, item);
}
return Enum.Parse(enumType, value.ToString());
}
}
Demo
Here is a working sample code based on the answer above
DataGridEnumsSample.zip (VS 2013)
MD5 Checksum: 9C34BB81857C78375582FAC0E1C8A95D
I have another solution that will allow you to display enums in a DataGrid using auto-generated columns. This works for enum types that are created using reflection.
First, create an EnumTemplateColumn that inherits from DataGridBoundColumn:
public class EnumTemplateColumn : DataGridBoundColumn
{
private readonly Type enumType;
public EnumTemplateColumn(Type enumType)
{
this.enumType = enumType;
}
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
string columnHeader = cell.Column.Header.ToString();
TextBlock textBlock = new TextBlock();
var dataRowView = (DataRowView) dataItem;
var enumValue = dataRowView[columnHeader];
textBlock.Text = Enum.GetName(this.enumType, enumValue);
return textBlock;
}
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
throw new NotImplementedException();
}
}
Next, use the OnAutoGeneratingColumn event of the DataGrid to use the EnumTemplateColumn:
private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyType.IsEnum)
{
e.Column = new EnumTemplateColumn(e.PropertyType)
{
Header = e.Column.Header,
};
}
}
And the WPF component:
<DataGrid x:Name="dataGrid"
Grid.Row="5"
ItemsSource="{Binding Path=DataTable}" IsReadOnly="True"
AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn"/>
This is how I did it: in the AutoGeneratingColumnevent of the DataGrid, replace the default DataGridComboBoxColumn by DataGridTextColumn, and add the binding and converter manually.
private void dataGrid1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyType.IsEnum)
{
//(e.Column as DataGridComboBoxColumn)
var col = new DataGridTextColumn
{
Header = e.PropertyName
};
col.Binding = new Binding(e.PropertyName)
{
Converter = new WPFCommon.BindingConverters.EnumConverter()
};
// Replace the auto-generated column with the new one.
e.Column = col;
}
}
The converter class,
namespace WPFCommon.BindingConverters
{
public class EnumConverter:IValueConverter
{
//** this does not work for enum value in DataTable
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrEmpty(value.ToString()))
return DependencyProperty.UnsetValue;
return ((Enum)value).GetDescription();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
}
The class for enum extension,
public static class EnumExtensions
{
public static string GetDisplayName(this Enum enu)
{
DisplayAttribute attr = GetDisplayAttribute(enu);
return attr != null ? attr.Name : enu.ToString();
}
public static string GetDescription(this Enum enu)
{
DescriptionAttribute attr = GetDescriptionAttribute(enu);
return attr != null ? attr.Description : enu.ToString();
}
private static DescriptionAttribute GetDescriptionAttribute(object value)
{
Type type = value.GetType();
if (!type.IsEnum)
{
throw new ArgumentException(string.Format("Type {0} is not an enum", type));
}
// Get the enum field.
var field = type.GetField(value.ToString());
return field == null ? null : field.GetCustomAttribute<DescriptionAttribute>();
}
private static DisplayAttribute GetDisplayAttribute(object value)
{
Type type = value.GetType();
if (!type.IsEnum)
{
throw new ArgumentException(string.Format("Type {0} is not an enum", type));
}
// Get the enum field.
var field = type.GetField(value.ToString());
return field == null ? null : field.GetCustomAttribute<DisplayAttribute>();
}
}
I have a set of classes with a set of properties like this; Each having a custom attribute indicating the possible values it could take. Is there anyway to databind these values to a combobox instead of hardcoding using <ComboBoxItem/> ?
[Values("Cash","Bank","Not Applicable")]
public Nullable<int> PaymentMethod{ get; set; }
Edit: My attribute looks like this
class ValuesAttribute:Attribute
{
public List<string> values { get; set; }
public ValuesAttribute(params String[] values)
{
this.values= new List<string>();
foreach (var v in values)
{
this.values.Add(v);
}
}
}
I would use a converter for this. Send it the underlying object and the property name as the parameter. Return a key/value array so that you can bind both the value (index/enum value) and display text:
<ComboBox ItemsSource="{Binding ConverterParameter='PaymentMethod',Converter={StaticResource AttributeConverter}}"
DisplayMemberPath="Value" SelectedValuePath="Key"
/>
The converter can then get the values using reflection:
public class AttributeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && parameter as string != null)
{
var property = value.GetType().GetProperty((string)parameter);
if (property != null)
{
var attribute = property.GetCustomAttributes(typeof(ValuesAttribute), false).OfType<ValuesAttribute>().FirstOrDefault();
if (attribute != null)
return attribute.values.Select((display, index) =>
new KeyValuePair<int, string>(index, display)
).ToArray();
}
}
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Note: if you need to do this a lot in your application, it might be worthwhile subclassing ComboBox, or creating a Behavior that applies the relevant properties.
I am using an enum to enlist values in my combobox.
I want to write a converter that would show the "description" of the selected enum value. And, when selected, it would return the enum value.
Most of the converters online have not implemented the ConvertBack() method (which is why I am posting here).
Here is ConvertBack method:
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
Full Converter Code:
public class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return DependencyProperty.UnsetValue;
return GetDescription((Enum)value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public static string GetDescription(Enum en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
return en.ToString();
}
}
EDIT
Here is my ComboBox XAML:
<ComboBox ItemsSource="{Binding SampleValues}"
SelectedItem="{Binding SelectedValue, Converter={StaticResource enumConverter}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=., Converter={StaticResource enumConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I know this is an old question, but for some reason, this is rather complicated, even though it seems like it'd be a pretty common task (I'm currently doing this on a UWP app). Using a combination of the accepted answer, some other items I found, and a bit of my own work, here's the simplest way I've found to accomplish this menial task. In short:
Define your enum along w/ setting the description in the Display attribute
Create a converter that converts from an enum value to the description
In your viewmodel, expose a collection of enum values from which to choose, the selected enum value, then initialize those
Define a couple of handy enum extension methods
Finally, some simple binding to the ComboBox, just overriding its ItemTemplate to use the converter.
Enum
public enum EnumOptions
{
[Display(Description = "Option 1")]
OptionOne= 1,
[Display(Description = "Option 2")]
OptionTwo,
[Display(Description = "Option 3")]
OptionThree
}
Converter
public class EnumToDisplayConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var enumValue = value as Enum;
return enumValue == null ? DependencyProperty.UnsetValue : enumValue.GetDescriptionFromEnumValue();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
}
}
Viewmodel (partial)
public IReadOnlyList<EnumOptions> Options { get; }
private EnumOptions _selectedOption;
public EnumOptions SelectedOption
{
get { return _selectedOption; }
set
{
_selectedOption = value;
OnPropertyChanged(() => SelectedOption);
}
}
// Initialization in constructor
Options = EnumExtensions.GetValues<EnumOptions>().ToArray();
// If you want to set a default.
SelectedOption = Options[0];
Extensions
public static class EnumExtensions
{
public static string GetDescriptionFromEnumValue(this Enum value)
{
var attribute = value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof(DisplayAttribute), false)
.SingleOrDefault() as DisplayAttribute;
return attribute == null ? value.ToString() : attribute.Description;
}
/// <summary>
/// Enumerates all enum values
/// </summary>
/// <typeparam name="T">Enum type</typeparam>
/// <returns>IEnumerable containing all enum values</returns>
/// <see cref="http://stackoverflow.com/questions/972307/can-you-loop-through-all-enum-values"/>
public static IEnumerable<T> GetValues<T>()
{
return Enum.GetValues(typeof (T)).Cast<T>();
}
}
XAML (partial)
<TextBlock Grid.Row="1">Choose an option</TextBlock>
<ComboBox Grid.Row="2"
ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource EnumToDisplayConverter}}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
[ValueConversion(typeof(MyEnum), typeof(String))]
public class MyEnumConverter : IValueConverter
{
public object Convert(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
var enumVal = (MyEnum)value;
// in this example, this is an extension method
return enumValue.Description();
}
public object ConvertBack(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
var enumDesc = value as string;
MyEnum val;
if (Enum.TryParse(typeof(MyEnum), strValue, out val))
{
return val;
}
return DependencyProperty.UnsetValue;
}
}
The extension method in the example might look like this:
public static string Description(this MyEnum enumVal)
{
// you could use a switch statement here;
// or maybe a Dictionary
}
Supplement to the above examples to show decorating the enum with attributes.
sealed class DescriptionAttribute : Attribute
{
readonly string description;
public DescriptionAttribute(string description)
{
this.description = description;
}
public string Description
{
get { return description; }
}
}
enum Vehicle
{
[Description("Benz")]
Car,
[Description("Volvo")]
Bus,
[Description("Honda")]
Bike
}
BTW, I wonder why you needed to convert back the description to enum. If you provide the enums itself as ItemSource, you can use the description technique to show the display value in the ComboBox, however, once an item is selected you can directly have an enum as selected item.
Here is my working well example:
Enum definition:
public enum MyEnum
{
[Description("Exchange 2007")]
E2007,
[Description("Exchange 2010")]
E2010,
[Description("Exchange 2013")]
E2013,
};
Helper class:
public static class cHelperClass
{
#region GetValuesAndDescriptions
public static object[] GetValuesAndDescriptions(Type enumType)
{
var kvPairList = new List<KeyValuePair<string, string>>();
var listValue = Enum.GetValues(enumType);
for (var i = 0; i < listValue.Length; i++)
{
var value = listValue.GetValue(i);
var enumValue = (Enum)listValue.GetValue(i);
kvPairList.Add(new KeyValuePair<string, string>(value.ToString(), GetDescription(enumValue)));
}
var valuesAndDescriptions = from kv in kvPairList select new
{
Value = kv.Key,
Description = kv.Value
};
return valuesAndDescriptions.ToArray();
}
public static string GetDescription(this Enum value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attributes.Length > 0) ? attributes[0].Description : value.ToString();
}
public static string GetStringValue(this Enum enumItem)
{
return enumItem
.GetType()
.GetField(enumItem.ToString())
.GetCustomAttributes<StringValueAttribute>()
.Select(a => a.Value)
.FirstOrDefault() ?? enumItem.ToString();
}
public static string GetName(Type enumType, object value)
{
return Enum.GetName(enumType, value);
}
#endregion
}
XAML:
<UserControl.Resources>
<!-- ObjectDataProvider für WindowStyles -->
<ObjectDataProvider x:Key="myEnumResource" MethodName="GetValuesAndDescriptions" ObjectType="classes:cHelperClass">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="classes:MyEnum" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</UserControl.Resources>
<ComboBox ItemsSource="{Binding Source={StaticResource myEnumResource}}" DisplayMemberPath="Description" SelectedValuePath="Value" SelectedValue="{Binding MyEnum, Mode=TwoWay}" />
I have a UserControl that uses a binding converter. I've made the converter an inner class of
public partial class MyPanel : UserControl
{
public class CornerRadiusConverter : IValueConverter
{
How do I reference the Converter class from the XAML? The following does not work:
<controls:MyPanel.CornerRadiusConverter x:Key="CornerRadiusConverter" />
It gives this error:
The tag
'LensPanel.CornerRadiusConverter' does
not exist in XML namespace
'clr-namespace:MyApp.Windows.Controls'
I was thinking about this problem again, and I came up with something similar to Dennis's solution : create a "proxy" converter class, with a Type property, which will create the instance of the actual converter and delegate the conversion to it.
public class Converter : IValueConverter
{
private Type _type = null;
public Type Type
{
get { return _type; }
set
{
if (value != _type)
{
if (value.GetInterface("IValueConverter") != null)
{
_type = value;
_converter = null;
}
else
{
throw new ArgumentException(
string.Format("Type {0} doesn't implement IValueConverter", value.FullName),
"value");
}
}
}
}
private IValueConverter _converter = null;
private void CreateConverter()
{
if (_converter == null)
{
if (_type != null)
{
_converter = Activator.CreateInstance(_type) as IValueConverter;
}
else
{
throw new InvalidOperationException("Converter type is not defined");
}
}
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
CreateConverter();
return _converter.Convert(value, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
CreateConverter();
return _converter.ConvertBack(value, targetType, parameter, culture);
}
#endregion
}
You use it like that :
<Window.Resources>
<my:Converter x:Key="CornerRadiusConverter" Type="{x:Type controls:MyPanel+CornerRadiusConverter}"/>
</Window.Resources>
It could be possible. A few months ago I wrote a markup extension to create the converter for you inline. It keeps a dictionary of weak references so that you don't create multiple instances of the same converter. Handles creating converters with different arguments too.
In XAML:
<TextBox Text="{Binding Converter={NamespaceForMarkupExt:InlineConverter {x:Type NamespaceForConverter:ConverterType}}}"/>
C#:
[MarkupExtensionReturnType(typeof(IValueConverter))]
public class InlineConverterExtension : MarkupExtension
{
static Dictionary<string, WeakReference> s_WeakReferenceLookup;
Type m_ConverterType;
object[] m_Arguments;
static InlineConverterExtension()
{
s_WeakReferenceLookup = new Dictionary<string, WeakReference>();
}
public InlineConverterExtension()
{
}
public InlineConverterExtension(Type converterType)
{
m_ConverterType = converterType;
}
/// <summary>
/// The type of the converter to create
/// </summary>
/// <value>The type of the converter.</value>
public Type ConverterType
{
get { return m_ConverterType; }
set { m_ConverterType = value; }
}
/// <summary>
/// The optional arguments for the converter's constructor.
/// </summary>
/// <value>The argumments.</value>
public object[] Arguments
{
get { return m_Arguments; }
set { m_Arguments = value; }
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
PropertyInfo propertyInfo = target.TargetProperty as PropertyInfo;
if (!propertyInfo.PropertyType.IsAssignableFrom(typeof(IValueConverter)))
throw new NotSupportedException("Property '" + propertyInfo.Name + "' is not assignable from IValueConverter.");
System.Diagnostics.Debug.Assert(m_ConverterType != null, "ConverterType is has not been set, ConverterType{x:Type converterType}");
try
{
string key = m_ConverterType.ToString();
if (m_Arguments != null)
{
List<string> args = new List<string>();
foreach (object obj in m_Arguments)
args.Add(obj.ToString());
key = String.Concat(key, "_", String.Join("|", args.ToArray()));
}
WeakReference wr = null;
if (s_WeakReferenceLookup.TryGetValue(key, out wr))
{
if (wr.IsAlive)
return wr.Target;
else
s_WeakReferenceLookup.Remove(key);
}
object converter = (m_Arguments == null) ? Activator.CreateInstance(m_ConverterType) : Activator.CreateInstance(m_ConverterType, m_Arguments);
s_WeakReferenceLookup.Add(key, new WeakReference(converter));
return converter;
}
catch(MissingMethodException)
{
// constructor for the converter does not exist!
throw;
}
}
}
What I do is:
<Window.Resources>
<ResourceDictionary>
<Converters:BooleanNotConverter x:Key="BooleanNotConverter"/>
</ResourceDictionary>
</Window.Resources>
And then in the control
<CheckBox IsChecked="{Binding Path=BoolProperty, Converter={StaticResource BooleanNotConverter} />