2
votes

BACKGROUND

I have a simple class that I've created that I use to population my collections that are assigned to my ItemSource property on my combo boxes. I use this when the source is a list of enumerations that need a good descriptor text.

public class BindableEnumerationItem<T> : INotifyPropertyChanged 
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    private T _item;

    public BindableEnumerationItem(T item, string displayText)
    {
        _item = item;
        _displayText = displayText;
    }

    private string _displayText;
    public string DisplayText
    {
        get { return _displayText; }
        set
        {
            if (value != _displayText)
            {
                _displayText = value;
                PropertyChanged(this, new PropertyChangedEventArgs("DisplayText"));
            }
        }
    }

    public T Item
    {
        get { return _item; }
        set
        {
            _item = value;
            PropertyChanged(this, new PropertyChangedEventArgs("Item"));
        }
    }
}

It's pretty straight forward.

Next, in my ViewModels I assign to List objects my lists of BindableEnumerationItem objects for my associated enumeration values. Something like this is often defined in my initialization code:

//DisplayAccountTypes is a property that I'll use as an ItemSource
DisplayAccountTypes = new List<BindableEnumerationItem<DisplayAccountType>>();
DisplayAccountTypes.Add(new BindableEnumerationItem<DisplayAccountType>(DisplayAccountType.Windows, "Windows Credentials"));
DisplayAccountTypes.Add(new BindableEnumerationItem<DisplayAccountType>(DisplayAccountType.SQL, "SQL Server"));
DisplayAccountTypes.Add(new BindableEnumerationItem<DisplayAccountType>(DisplayAccountType.Linux, "Linux"));

Next, I create a ComboBox in my xaml. Assume that the above initialization snippet is from within a View Model that is the DataContext object on my xaml object.

<ComboBox x:Name="ComboboxAccountType" 
    ItemsSource="{Binding DisplayAccountTypes}"
    SelectedItem="{Binding SelectedDisplayAccountType}"
    DisplayMemberPath="DisplayText"
    SelectedValuePath="Item" >

FYI, SelectedDisplayAccountType is specifically an enumeration property on my VM.

PROBLEM

Now, I've used this setup time and time again. This has worked fine. I can toggle the combo box values and it updates on the backend. However, on my most recent implementation I've been getting the following errors when I attempt to use the current combo box.

System.Windows.Data Error: 23 : Cannot convert MyApp.ViewModel.Helpers.BindableEnumerationItem`1[MyApp.ViewModel.DisplayAccountType] from type BindableEnumerationItem`1 to type MyApp.ViewModel.DisplayAccountType for 'en-US' culture with default conversions; consider using Converter property of Binding. NotSupportedException:'System.NotSupportedException: EnumConverter cannot convert from MyApp.ViewModel.Helpers.BindableEnumerationItem`1[[MyApp.ViewModel.DisplayAccountType, MyApp.ViewModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. at System.ComponentModel.TypeConverter.GetConvertFromException(Object value) at System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value) at System.ComponentModel.EnumConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value) at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)'

System.Windows.Data Error: 7 : ConvertBack cannot convert value MyApp.ViewModel.Helpers.BindableEnumerationItem`1[MyApp.ViewModel.DisplayAccountType] (type BindableEnumerationItem`1). BindingExpression:Path=SelectedDisplayAccountType; DataItem='CredentialsManagementViewModel' (HashCode=58609412); target element is 'ComboBox' (Name='ComboboxAccountType'); target property is 'SelectedItem' (type 'Object') NotSupportedException:'System.NotSupportedException: EnumConverter cannot convert from MyApp.ViewModel.Helpers.BindableEnumerationItem`1[[MyApp.ViewModel.DisplayAccountType, MyApp.ViewModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward) at MS.Internal.Data.ObjectTargetConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture) at System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)'

This has stumped me for hours. I'm not doing anything special. In fact, the above code is the broken production code. I could copy & paste another example of my use of this setup, however, they are pretty much the EXACT same thing except name/values are different for each property/selectors respective context.

I understand that these errors are saying but, in regards to what I'm doing, they don't make sense. I'm not trying to convert from a BindableEnumerationItem to my enumeration. I should be binding to the Item property on my BindableEnumerationItem which should certainly be my enumeration.

Maybe I just need a second pair of eyes, but I can't spot the problem. Thank you.

1
I guess the problem is that your DisplayAccountType is not part of BindableEnumerationItem because you are using a generic type. If,instead of public T Item you use public DisplayAccountType Item,I think it should work.You may try it to discard this.Pikoh
I don't think the generic approach is a problem at all. I use the same. Have you tried to use SelectedValueinstead o SelectedItem?Mat
@Mat ...and when you see it, it's so obvious. This is why I needed those second pair of eyes. In all other locations, I was using SelectedValue rather than SelectedItem. Changing the reference has fixed my application. Answer the question below to get the answer mark. Thanks again.RLH
@will see my last comment. Mat spotted the real issue.RLH

1 Answers

2
votes

The problem is that your items are of type BindableEnumerationItem<DisplayAccountType>, but you bind the SelectedItem to a property of type DisplayAccountType.

There are four solutions:

  1. Use SelectedIndex instead.
  2. Use SelectedValue instead.
  3. Make DisplayAccountTypes to be a collection of DisplayAccountType items.
  4. Make SelectedDisplayAccountType to be of type BindableEnumerationItem<DisplayAccountType>.