Binding SelectedValue of ComboBox to enum in WPF

允我心安 提交于 2020-01-12 10:12:09

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!