Gavin
Gavin

Reputation: 1233

Windows Phone 7 ListPicker InvalidCastException

I have a problem building a simple crud form in WP7. I have spent a lot of time to display an Enum into a listpicker an now I see InvalidCastException when trying to bind to the (IsolatedStorage) object.

public class Bath {
   public string Colour { get; set; }
   public WaterType WaterType { get; set; }
}

public enum WaterType {
   Hot,
   Cold
}

The enum is bound to a ListPicker, but as there is not enum.GetValues() in WP7 this is not a simple task.

I have a simple type class...

public class TypeList
    {
        public string Name { get; set; }
    }

And in my viewmodel, I have ObservableCollection and mock the values from the enum...

    private ObservableCollection<TypeList> _WaterTypeList;
    public ObservableCollection<TypeList> WaterTypeList
    {
        get { return _WaterTypeList; }
        set
        {
            _WaterTypeList= value;
            NotifyPropertyChanged("WaterTypeList");
        }
    }

    public void LoadCollectionsFromDatabase()
    {
        ObservableCollection<TypeList> wTypeList = new ObservableCollection<WaterTypeList>();
        wTypeList.Add(new TypeList{ Name = WaterType.Hot.ToString() });
        wTypeList.Add(new TypeList{ Name = WaterType.Income.ToString() });
        WaterTypeList = new ObservableCollection<TypeList>(wTypeList);
    }

Finally, my xaml contains the listbox...

<toolkit:ListPicker
            x:Name="BathTypeListPicker"
            ItemsSource="{Binding WaterTypeList}"
            DisplayMemberPath="Name">
        </toolkit:ListPicker>

Im not sure if the above is best practise and indeed if the above is part of the problem but the above does give me a populated ListPicker.

Finally, when the form is submitted the cast causes a InvalidCastException.

 private void SaveAppBarButton_Click(object sender, EventArgs e)
{
    var xyz = WaterTypeList.SelectedItem; // type AppName.Model.typeList

        Bath b = new Bath
        {
            Colour = ColourTextBox.Text ?? "Black",
            WaterType = (WaterType)WaterTypeListPicker.SelectedItem
        };

        App.ViewModel.EditBath(b);
        NavigationService.Navigate(new Uri("/Somewhere.xaml", UriKind.Relative));
    }
}

Has anyone faced a simlar problem and can offer advice. I see that my opions are to concentrate on casting something meaningful from the ListPicker or should I rethink the way that the ListPicker is populated?

Upvotes: 2

Views: 456

Answers (2)

Stuart
Stuart

Reputation: 66882

The WaterTypeListPicker.SelectedItem is an object of type TypeList so it can't be cast to an object of type WaterType.

In order to convert back to a WaterType, you could replace your cast:

WaterType = (WaterType)WaterTypeListPicker.SelectedItem

with:

WaterType = (WaterType)Enum.Parse(
                               typeof(WaterType),
                               ((TypeList)WaterTypeListPicker.SelectedItem).Name,
                               false)

Upvotes: 1

123 456 789 0
123 456 789 0

Reputation: 10865

As far as I can see, WaterTypeList is an ObservableCollection that is a Type of and an observable collection doesn't have a SelectedItem property.

Your Bath class has a WaterType that accepts WaterType property and you are trying to cast a WaterTypeListPicker.SelectedItem to it.. so I'm assuming your WatertypeListPicker is your ListBox?

If it is, then you are doing it wrong because your ListBox's itemssource is bound to a class and you are trying to add a to your WaterType Property.

What I would do is say,

Bath b = new Bath
        {
            Colour = ColourTextBox.Text ?? "Black",
            WaterType = WaterTypeListPicker.SelectedItem
        };

Either change my property of Bath's WaterType to a TypeList so the above code would work. But I won't recommend doing another class to wrap the enum just to show it to the listbox.

What I would do is create an EnumHelper

public static class EnumExtensions
{
    public static T[] GetEnumValues<T>()
    {
        var type = typeof(T);
        if (!type.IsEnum)
            throw new ArgumentException("Type '" + type.Name + "' is not an enum");

        return (
          from field in type.GetFields(BindingFlags.Public | BindingFlags.Static)
          where field.IsLiteral
          select (T)field.GetValue(null)
        ).ToArray();
    }

    public static string[] GetEnumStrings<T>()
    {
        var type = typeof(T);
        if (!type.IsEnum)
            throw new ArgumentException("Type '" + type.Name + "' is not an enum");

        return (
          from field in type.GetFields(BindingFlags.Public | BindingFlags.Static)
          where field.IsLiteral
          select field.Name
        ).ToArray();
    }
}

And Bind it to a Collection

My ViewModel

public IEnumerable<string> Priority
        {
            get { return EnumExtensions.GetEnumValues<Priority>().Select(priority => priority.ToString()); }

public string SelectedPriority
        {
            get { return Model.Priority; }
            set { Model.Priority = value; }
        }

Like that.

My XAML.

<telerikInput:RadListPicker SelectedItem="{Binding SelectedPriority, Mode=TwoWay}" ItemsSource="{Binding Priority}" Grid.Column="1" Grid.Row="4"/>

Upvotes: 3

Related Questions