4

Xamarin Forms – MVVM binding translated Enum values

 3 years ago
source link: https://depblog.weblogs.us/2020/02/25/xamarin-forms-mvvm-binding-translated-enum-values/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Xamarin Forms – MVVM binding translated Enum values

I once did a post about how to bind Enum values and use translation in the beginning of Windows Phone development… But seeing several questions on Stack Overflow about it in other programming environments, let me work this out again using Xamarin Forms this time.

So the general idea is, that in some sort of data entry page, you want the user to be able to select a value from a list. This list contains several items, all based on a given Enum you defined.

The Enum defined in this example is very simpel, it could be used to let the user pick a t-shirt size…

public enum Size
    Small,
    Medium,
    Large

Of course straight up displaying the Enum values to the user would often not be ideal, you want to format them into some readable text. Also maybe your app needs to run globally, so potentially translations will be needed.
Best way to tackle this, is by using Resource files in Xamarin Forms.

How you deal with translations and Resource files ( resx ) in Xamarin forms is very well documented here https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/localization/text. So best to go through that first before continuing here.

To make my example with enum translation work, we need a format that we will use for the keys of our values inside the Resource files. In this repository I went for the combination of {Enum type name}_{Enum value}.
Resulting in following Resource files content.
English

<data name="Size_Small" xml:space="preserve">
    <value>Small</value>
</data>
<data name="Size_Medium" xml:space="preserve">
    <value>Medium</value>
</data>
<data name="Size_Large" xml:space="preserve">
    <value>Large</value>
</data>

Dutch

<data name="Size_Small" xml:space="preserve">
    <value>Klein</value>
</data>
<data name="Size_Medium" xml:space="preserve">
    <value>Normaal</value>
</data>
<data name="Size_Large" xml:space="preserve">
    <value>Groot</value>
</data>

When using Resource files correctly, we will have a generated class called TranslationResources available that we can use to retrieve translated values.

After this, we are going to set up our data representation. We would like to have our CollectionView automagically binded to our Enum, in such a way that it will use the translated Enum display values instead of the corresponding Enum values. The Page xaml for it would be

<StackLayout Margin="10,40,10,20">
    <Label Text="Enum binding and translation"
            HorizontalOptions="FillAndExpand" HorizontalTextAlignment="Center" />
    <Label Text="{Binding SelectedItemLabel}"
            Margin="0,10,0,10"/>
    <CollectionView x:Name="SizeSelection"
                    SelectionMode="Single"
                    SelectedItem="{Binding SelectedSize}"
                    ItemsSource="{Binding SelectionType, Converter={StaticResource EnumTypeToItemsSourceConverter}}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Label Text="{Binding Value}" Padding="0,10,0,10" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</StackLayout>

The thing to notice in here, is the fact that the ItemsSource of the CollectionView is binding to a .Net Type instead of some actual data collection.

private Type _selectionType;
public Type SelectionType
    get => _selectionType;
        _selectionType = value;
        OnPropertyChanged();

To get values displayed, we of course need to convert that type to something the CollectionView can understand, hence the use of a Converter.
The code of the Converter looks like this

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    if (value == null)
        return null;
    var enumType = value as Type;
    if (enumType == null || !enumType.GetTypeInfo().IsEnum)
        return null;
    var values = Enum.GetValues((Type)value).Cast<Enum>();
    return values.Select((enumValue, i) => new EnumItem()
        Index = i,
        Value = TranslationResources.ResourceManager.GetString($"{enumType.Name}_{enumValue}")
    }).ToList();

The input parameter for the Converter should be a .Net Enum type, so we check if that is the case. If so we get all the values of the given Enum and pass those items through the auto generated TranslationResources class to get the correctly translated display value for the each Enum value. In the end we transform this to a List so the CollectionView can use this for displaying.
Another thing to note, is that we als keep track of the Enum index. We will need that later on to verify which item was selected in the list.

Once the list is presented to the user, we again want to capture the actual Enum value, because that would be the value we need to store in a database or pass to an API. The SelectedItem binding of the CollectionView will take care of this!

private EnumItem _selectedSize;
public EnumItem SelectedSize
    get => _selectedSize;
        _selectedSize = value;
        if (_selectedSize != null)
            SelectedItemLabel = $"You selected enum value: {Enum.GetValues(this.SelectionType).GetValue(_selectedSize.Index).ToString()}";
            SelectedItemLabel = "You have not yet selected an item from the list";
        OnPropertyChanged();

Once an item is selected we use the index that is passed along to get the real Enum value, for presenting it on screen I .ToString() it. But that would not be needed if you just want to store the value.
Of course you could also just put the original value inside the EnumItem class instead of using the index…

The result would be as follows and as always the code can be found on my GitHub…

EnumBindingTranslation.gif

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK