1

I am attaching enum to a picker and onSelect i am binding to the actual value of the enum, not its title. My enum is as follows:

 public enum Reason
    {
        AnnualLeave = 12,
        Emergency = 23,
        MaternityLeave = 34
    }

My class uses the following to bind the enum title to the picker

        public Reason ReasonSelectedOption { get; set; }

        public ObservableCollection<Reason> ReasonDisplay
        {
            get => new ObservableCollection<Reason>(Enum.GetValues(typeof(Reason)).OfType<Reason>().ToList());
        }

The actual picker

 <Picker
       ItemsSource="{Binding ReasonDisplay}"
       SelectedItem="{Binding ReasonSelectedOption}"
       Title="Please Select"
       HorizontalOptions="FillAndExpand" />

Everything works fine except in the actual picker, the options appear as AnnualLeave and MaternityLeave which is what's expected from my code but i want them to appear as Annual Leave and Maternity Leave (with the space inbetween) preserving the selecteditem value

Current case: When user selects AnnualLeave, selectedItem value is 12, if i convert to string the selected value becomes 0.

I am simply asking how to put spaces inbetween the enum options and also preserve the SelectedItem integer value

tendai
  • 1,172
  • 1
  • 11
  • 22
  • 1
    Does this answer your question? [WPF Binding a ListBox to an enum, displaying the Description Attribute](https://stackoverflow.com/questions/3985876/wpf-binding-a-listbox-to-an-enum-displaying-the-description-attribute) – Neil May 12 '20 at 09:07
  • Thanks @Neil, the given answer doesn't seem to do the trick for Xamarin – tendai May 12 '20 at 10:05

2 Answers2

1

You could use a converter

public class EnumToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) return null;
        var valueAsString = value.ToString();            
        valueAsString = valueAsString.SplitCamelCase();
        return valueAsString;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

And for the SplitCamelCase I wrote this but I'm sure there are cleaner options:

public static string SplitCamelCase(this string str)
    {
        string result = "";
        for (int i = 0; i < str.Count(); i++)
        {
            var letter = str[i];
            var previousLetter = i != 0 ? str[i - 1] : 'A';
            if (i != 0 && char.IsUpper(previousLetter) == false && char.IsUpper(letter)) result = result + " " + letter;
            else result = result + letter;
        }
        return result;
    }

Then just used it like so:

<TextBlock Text="{Binding Converter={StaticResource EnumToStringConverter}}"/>
Yosef Bernal
  • 1,006
  • 9
  • 20
  • Thank you for your response, could you please edit your answer to match my given scenario. I have tried and can't seem to get it working – tendai May 12 '20 at 10:03
1

Here you have to keep in mind the internationalisation.

Even if you don't have localised texts now, you may have to support it in the future. So, keeping that in mind, you won't need simply to "split" the string, but to take a specific text from somewhere (i.e. translate it according to the culture).

You can achieve it with the help of some attributes, extension methods and some clever binding.

Let's say that you want to have a picker with 2 options - what is the property type. The PropertyType is an enum, that looks like this:

public enum PropertyType
{
    House,
    Apartment
}

Since the built-in Description attribute can't translate texts for us, we can use a custom attribute to assign a specific text to an enum type, like this:

public enum PropertyType
{
    [LocalizedDescription(nameof(R.SingleFamilyHouse))]
    House,
    [LocalizedDescription(nameof(R.ApartmentBuilding))]
    Apartment
}

The attribute code looks like this:

public class LocalizedDescriptionAttribute : DescriptionAttribute
{
    private readonly ResourceManager resourceManager;
    private readonly string resourceKey;

    public LocalizedDescriptionAttribute(string resourceKey, Type resourceType = null)
    {
        this.resourceKey = resourceKey;

        if (resourceType == null)
        {
            resourceType = typeof(R);
        }
        resourceManager = new ResourceManager(resourceType);
    }

    public override string Description
    {
        get
        {
            string description = resourceManager.GetString(resourceKey);
            return string.IsNullOrWhiteSpace(description) ? $"[[{resourceKey}]]" : description;
        }
    }
}

R is my resx file. I have created a Resources folder and inside it I have 2 resx files - R.resx (for English strings) & R.de.resx (for German translation). If you don't want to have internationalisation now, you can change the implementation to get your strings from another place. But it is considered a good practice to always use a resx file, even if you only have 1 language. You never now what tomorrow may bring.

Here is my structure: enter image description here

The idea behind LocalizedDescriptionAttribute class is that the built-in Description attribute isn't very useful for our case. So we'll have to take the resource key that we have provided it, translates and to override the Description attribute, which later we will reference.

Now we need to obtain the localised description text with this helper method:

public static class EnumExtensions
{
    public static string GetLocalizedDescriptionFromEnumValue(this Enum value)
    {
        return !(value.GetType()
            .GetField(value.ToString())
            .GetCustomAttributes(typeof(LocalizedDescriptionAttribute), false)
            .SingleOrDefault() is LocalizedDescriptionAttribute attribute) ? value.ToString() : attribute.Description;
    }
}

Now, when we create the bindings for the Picker, we won't just use a simple Enum, but a specific PropertyTypeViewModel, which will have 2 properties - the Enum itself and a Name that will be displayed.

public class PropertyTypeViewModel : BaseViewModel
{
    private string name;
    public string Name
    {
        get => name;
        set => SetValue(ref name, value);
    }

    private PropertyType type;
    public PropertyType Type
    {
        get => type;
        set => SetValue(ref type, value);
    }

    public PropertyTypeViewModel()
    {
    }

    public PropertyTypeViewModel(PropertyType type)
        : this()
    {
        Type = type;
        Name = type.GetLocalizedDescriptionFromEnumValue();
    }
}

The important line is the last one - Name = type.GetLocalizedDescriptionFromEnumValue();

The final thing that is left is to set your Picker's ItemsSource to your collection of PropertyTypeViewModels and ItemDisplayBinding to be pointing to the Name property - ItemDisplayBinding="{Binding Name}"

That's it - now you have a Picker with dynamic localised strings.

Mihail Duchev
  • 4,691
  • 10
  • 25
  • 32
  • Thank you for you detailed response. I noticed the line resourceType = typeof(R); - what is R here? – tendai May 12 '20 at 11:42
  • Did you manage to make it work. If so, please mark the answer with a check mark so that it can help others. If you have any more questions, please don't hesitate to ask. :) – Mihail Duchev May 13 '20 at 09:44