Xamarin.Forms: Use converters with binding objects which can have null as value.

You have a converter, which should return a specific value, even if the binded object is null.

Converters does not get executed, when the binded value is null. But here is a solution, how to handle this situations.

Define an example converter

In this case we will use an Int value to bool value converter.

    public class IsIntGreaterThanConverter : IValueConverter, IMarkupExtension
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is int bindedValue && parameter is int targetValue)
            {
                return bindedValue > targetValue;
            }
            else
                return false;
        }

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

        public object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }

This code returns true, if the binded value is greater than the value provided as a converter parameter.

It can be used with the following code snippet in XAML:

            <Label Text="The value is greater than 0">
                <Label.IsVisible>
                    <Binding Path="Object.IntValue"
                             Converter="{app1:IsIntGreaterThanConverter}">
                        <Binding.ConverterParameter>
                            <x:Int32>0</x:Int32> 
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.IsVisible>
            </Label>

But what happends, when the ‘Object’ is null? Well the converter does not get executed. And since View’s default value of the IsVisible bindable property is true, then the label will be visible, even if the Object is null.

How to handle null scenarios?

Bindings have a property called FallbackValue. Give a value to the fallbackvalue property in order to override the default value, like this:

           <Label Text="The value is greater than 0">
                <Label.IsVisible>
                    <Binding Path="Object.IntValue"

                             FallbackValue="False"

                             Converter="{app1:IsIntGreaterThanConverter}">
                        <Binding.ConverterParameter>
                            <x:Int32>0</x:Int32> 
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.IsVisible>
            </Label>

This should return false even if the binded obejct is null. 🙂

if(true) else ágba futásának története

Először nem hittem a szememnek, amikor megláttam..

A fenti példa szemlélteti, hogy két megegyező típusú, és megegyező értékű enum a debug folyamat során quick watchban egyenlőségvizsgálatnál igazra értékelődik ki, de mégis az else ágba fut a debug folyamatban lévő kód tovább.

A fenti videón látható részlet egy XAML converter létrehozásánál keletkezett, ahol az egyenlőségvizsgálat egy boolean értékkel kellett hogy visszatérjen. A feladat az volt, hogyha a ViewModellen található UIMode property értéke egy meghatározott értéket vesz fel, akkor a láthatósága bizonyos kontrolloknak ennek megfelelően állítódjon.
Jó ötletnek tűnt persze, hogy Convertert írunk a Bindable propertyre, ami paraméterben megkapja x:Static-kal, hogy milyen értéknek kell megfelelnie.

Jött a megvalósítás, a rutinfeladat, hiszen nem ez az első converter amit pályafutásom alatt már megírtam, hittem én. Majd szembesültem a ténnyel, hogy az az újonnan kitalált funkció integráció utáni tesztelési folyamatában közel sem úgy működik a dolog, ahogy azt elterveztem.

Ilyenkor persze jönnek a szokásos gyógyegér dologra való rámondásos orvoslásoknak tűnő dolgok: Clean solution, app törtlés mobilról, majd miután ez sikertelen, azután Force clean solution (ez a bin/debug obj/debug mappák fizikai törlését jelenti), VS restart, majd végül gép restart, de egyik sem hozott eredményt.

A probléma forrása (Ha gondolkozni szeretnél még azon, hogy mi lehetett a baj, ne olvasd tovább! 🙂 )

Magának az újonnan létrehozott Converternek, hogy használható legyen konverterként, az IValueConverterből kellett származnia, amely megköveteli, a Convert, és ConvertBack metódusok implementációját. Mivel ezeknek a metódusoknak a szignatúrája objectként várja át a value-t, és a paramétert, ezért az érték típusú változók boxingolódnak.

Mivel runtime az object equals fut meg rájuk, hamisra fog kiértékelődni a condition, és ezért esik az else ágba a kód.

Hogy a Visual Studio a debug folyamat során a watchban hogyan jön rá arra, hogy mégsem az object equalst kéne rajtuk végrahjtani, nem jártam utána, ettől függetlenül is kissé idegtépő volt a dolog.