问题
I want to show list of products in ListView
where one of the columns is a ComboBox
that I want to bind. This is my enum:
public enum SelectionMode { One, Two }
And Product class:
public class Product
{
public SelectionMode Mode { get; set; }
public string Name { get; set; }
}
In ViewModel
class I have an ObservableCollection
of Product
's:
private ObservableCollection<Product> _productList;
public ObservableCollection<Product> ProductList
{
get
{
return _productList;
}
set
{
_productList = value;
}
}
public MainViewModel()
{
ProductList = new ObservableCollection<Product>
{
new Product {Mode = SelectionMode.One, Name = "One"},
new Product {Mode = SelectionMode.One, Name = "One"},
new Product {Mode = SelectionMode.Two, Name = "Two"}
};
}
And finally I have a Grid
with a ListView
that binds to my ProductList
:
<Window.Resources>
<ObjectDataProvider x:Key="AlignmentValues"
MethodName="GetNames" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="ViewModel:SelectionMode" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<ListView Height="120" HorizontalAlignment="Left"
VerticalAlignment="Top"
SelectionMode="Multiple"
ItemsSource="{Binding ProductList}" >
<ListView.View>
<GridView>
<GridViewColumn Width="120" Header="Product Name" DisplayMemberBinding="{Binding Path=Name}" />
<GridViewColumn Header="Selection Mode">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource AlignmentValues}}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
My question is; what is the way to bind SelectedValue
of ComboBox
to SelectionMode
property of my Product
class?
Update
Well. I found an answer in this topic. So I have to add converter class:
public class MyEnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (SelectionMode)Enum.Parse(typeof(SelectionMode), value.ToString(), true);
}
}
And add it to window resources:
<Window.Resources>
<ObjectDataProvider x:Key="AlignmentValues"
MethodName="GetNames" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="ViewModel:SelectionMode" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<Converters:MyEnumToStringConverter x:Key="MyEnumConverter"/>
</Window.Resources>
And finally edit ComboBox
data template:
<ComboBox ItemsSource="{Binding Source={StaticResource AlignmentValues}}"
SelectedValue="{Binding Path=Mode, Converter={StaticResource MyEnumConverter}}"/>
That's all. Hope it will be useful for someone else :)
回答1:
If you are ready to change the binding of the ItemsSource of the ComboBox then, simply SelectedValue="{Binding Mode,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
will work.
In this case you have to bind the ItemsSource like this: ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ViewClass}}, Path=ModeList}"
; where, the ModeList is a simple public property of list of SelectionMode
type, contains the enums which should be displayed in ComboBox dropdown and ViewClass is the class where this property (ModeList) is a available; make sure the reference of the namespace is added in the xaml.
Otherwise you have to use a converter, which should convertback the string to the enum type.
回答2:
Heres my usage of binding enums to a list/combox
public enum EnumsAvailable
{
[Description("Its an A")]
a,
[Description("Its a B")]
b,
[Description("Its a C")]
c,
[Description("Its a D")]
d
} ;
Here is my XAML
<ComboBox Grid.Column="4" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="cb_Application" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Path=ListOfEnumValues,Converter={converters:ArrayStringToEnumDescriptionConverter}}"
SelectedValue="{Binding Path=ChosenEnum,Converter={converters:DescriptionToEnumConverter},UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{x:Null}" TabIndex="5" />
My View Model
public EnumsAvailable ListOfEnumValues
{
get { return new EnumsAvailable(); }
}
public EnumsAvailable ChosenEnum {
get { return _ChosenEnum; }
set
{
if (_ChosenEnum != value)
{
_ChosenEnum = value;
RaisePropertyChanged(() => ChosenEnum);
}
}
}
and my convertors
public class ArrayStringToEnumDescriptionConverter : BaseEnumDescriptionConverter, IValueConverter
{
public ArrayStringToEnumDescriptionConverter()
{
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var type = value.GetType();
return !type.IsEnum ? null : base.GetEnumDescription(type);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
public abstract class BaseEnumDescriptionConverter : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public IEnumerable<string> GetEnumDescription(Type destinationType)
{
var enumType = destinationType;
var values = RetrieveEnumDescriptionValues(enumType);
return new List<string>(values);
}
public object GetEnumFromDescription(string descToDecipher, Type destinationType)
{
var type = destinationType;
if (!type.IsEnum) throw new InvalidOperationException();
var staticFields = type.GetFields().Where(fld => fld.IsStatic);
foreach (var field in staticFields)
{
var attribute = Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attribute != null)
{
if (attribute.Description == descToDecipher)
{
return (Enum.Parse(type, field.Name, true));
}
}
else
{
if (field.Name == descToDecipher)
return field.GetValue(null);
}
}
throw new ArgumentException("Description is not found in enum list.");
}
public static string[] RetrieveEnumDescriptionValues(Type typeOfEnum)
{
var values = Enum.GetValues(typeOfEnum);
return (from object fieldInfo in values select DescriptionAttr(fieldInfo)).ToArray();
}
public static string DescriptionAttr(object enumToQuery)
{
FieldInfo fi = enumToQuery.GetType().GetField(enumToQuery.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : enumToQuery.ToString();
}
public static string GetDomainNameAttribute(object enumToQuery)
{
FieldInfo fi = enumToQuery.GetType().GetField(enumToQuery.ToString());
DomainNameAttribute[] attributes = (DomainNameAttribute[])fi.GetCustomAttributes(
typeof(DomainNameAttribute), false);
return attributes.Length > 0 ? attributes[0].DomainName : enumToQuery.ToString();
}
}
public class DescriptionToEnumConverter : BaseEnumDescriptionConverter, IValueConverter
{
public DescriptionToEnumConverter(){}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var enumValue = value;
if (enumValue != null)
{
enumValue = GetEnumFromDescription(value.ToString(), targetType);
}
return enumValue;
}
}
In my personal opinion its alot cleaner and highly re-usable. The only flaw I've found so far with it is if you update the selected value in code(not via UI) then its not updating on the UI. But this can be overcome with some further UI tweaking. I'm doing the change now and I'll update this answer once I've completed it.
回答3:
i'm using a Converter for this, which also allows to define a string that will be displayed instead of the enum value: http://www.ageektrapped.com/blog/the-missing-net-7-displaying-enums-in-wpf/
来源:https://stackoverflow.com/questions/8644350/binding-selectedvalue-of-combobox-to-enum-in-wpf