So a recent Xamarin.Forms update released the new Bindable Picker
, which allows you to bind an IList
of objects
to the picker (which will be ToString()
‘ed). However, I’ve often find myself needing to create a form for a model that has enum
properties. Previously, in order to do this, I would have to create a custom List
or string
from my enum and map it manually, then read from the SelectedItem
bound to another string
property, then when I need the actual value I’d have to map it back to the enum it “represents”.
It might have looked something like this:
MyViewModel.cs
... private DogBreed _breedEnum; // this is our enum of: BorderCollie, LabradorRetriever, PitBull, etc. public List<string> BreedNames { get { return new List<string> { "Border Collie", "Labrador Retriever", "Pit Bull" }; } } private string _selectedBreed; public string SelectedBreed { get { return _selectedBreed; } set { Set(ref _selectedBreed, value); // this is using MvvmLight } } public void DoSomethingWithTheBreed() { switch(SelectedBreed) { case "Border Collie": _breedEnum = DogBreed.BorderCollie; break; case "Labrador Retriever": _breedEnum = DogBreed.LabradorRetriever; break; case "Pit Bull": _breedEnum = DogBreed.PitBull; break; //... } DoSomething(_breedEnum); } ...
And our XAML
<Picker ItemsSource="{Binding BreedNames}" SelectedItem="{Binding SelectedBreed}"/>
As you can see, this is pretty gross…
Here’s a quick little strategy I use to make the binding process a little easier with my enums
. It’s broken into just 3 quick parts:
- Create a extension methods to get a readable string from our `enum`
- Create a `Converter` to convert the `SelectedIndex` to the `enum` field
- Wire up the fields and XAML
Let’s create our enum extension methods to get a readable string for the UI:
StringExtensions.cs
public static class StringExtensions { public static string SplitCamelCase(this string str) { return Regex.Replace( Regex.Replace( str, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2" ), @"(\p{Ll})(\P{Ll})", "$1 $2" ); } }
This SplitCamelCase
method will take a string that is camel cased and split it out into separate words such as `”ThisIsMyValue”.SplitCamelCase(); // “This Is My Value”
Now that we have the ability to get a readable string from the enum
values, let’s create our ViewModel
properties we will need.
MyViewModel.cs
... private DogBreed _selectedBreed; public DogBreed SelectedBreed { get { return _selectedBreed; } set { Set(ref _selectedBreed, value); } } public List<string> BreedNames { get { return Enum.GetNames(typeof(DogBreed)).Select(b => b.SplitCamelCase()).ToList(); } } public void DoSomethingWithBreed() { DoSomething(SelectedBreed); } ...
So much cleaner already. Now we need to create a Converter
that our XAML can use to actually set the SelectedBreed
property of our ViewModel
.
IntEnumConverter.cs
public class IntEnumConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is Enum) { return (int)value; } return 0; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if(value is int) { return Enum.ToObject(targetType, value); } return 0; } }
Last thing to do is use our IntEnumConverter
and our properties to create our view in XAML:
MyView.xaml
<ContentPage.Resources> <ResourceDictionary> <converters:IntEnumConverter x:Key="IntEnum"/> </ResourceDictionary> </ContentPage.Resources> <Picker ItemsSource="{Binding BreedNames}" SelectedIndex="{Binding SelectedBreed, Converter=IntEnum}"/>
Here’s what we have!
Now you have the means to bind any of your Pickers
quite easily to any of your custom enum
fields!
“Woah! How did you get that Material Design Looking Picker on iOS”… Stay tuned!
If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!
Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.
Well done ! Thanks.
LikeLike
Nicely done. Thanks very much.
I had a bit of trouble trying to utilized the converter against the pick in XAML based on your example below:
I added the converter in the App.xaml.cs as a static resource instead and referenced it as follows:
App.xaml
XamlPage.cs
Again, thanks a lot for a much cleaner/nicer approach.
LikeLike
Thank you for checking out my posts!
LikeLike
Getting this error
[0:] Binding: 0 can not be converted to type
for the Selected. Any idea?
LikeLike
Hi! I’m getting the following error :
[0:] Binding: 0 can not be converted to type ‘Models.RelationShipType’
RelationShipType is the type of the Selected property on the picker.
Any ideas?
LikeLike
You should be binding the SelectedIndex property to your enum field with the converter as well. Check out the example at the bottom of the post to see how I did it
LikeLike
Exactly what I was looking for and worked Perfectly for my purpose. Thanks 🙂
LikeLike
Fantastic!
LikeLike
Nice one!
Alternatively, you can use a description attribute for each enum value and initialize strings List with them. This trick helps you to get rid of a dependency on an identifier way of writing.
LikeLike
What’s the point of using an enum if you’re just going to pass around string constants anyways? lol??
The correct way to do this is to bind the dropdown to use the enum for values, and then use a formatter to format the displayed value. You can get all the values of an enum with Enum.GetValues()
LikeLike
Thats almost exactly what we are doing here… you should re-read the whole article. The first block of code is what I commonly saw. The second is switching to using only the enum and an extension to get a cleaner display name
LikeLike
You’re right, I should read the whole thing before getting out the pitchforks
sorry
LikeLike