Walter W Kennedy IV Walter W Kennedy IV - 1 month ago 19
Android Question

Set LinearLayout background color via boolean value

I'm trying to use an MvxValueConverter to set the background color of a LinearLayout based on a boolean value. The converter looks like this:

public class BackgroundColorValueConverter : MvxValueConverter<bool, MvxColor>
{
private static readonly MvxColor TrueBGColor = new MvxColor(0xDB, 0xFF, 0xCE);
private static readonly MvxColor FalseBGColor = new MvxColor(0xD6, 0xF6, 0xFF);

protected override MvxColor Convert(bool value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value ? TrueBGColor : FalseBGColor;
}
}


In my AXML layout, I have the following code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="BackgroundColor MyBooleanValue, Converter=BackgroundColor">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18dp"
local:MvxBind="Text MyText" />
</LinearLayout>


I'm getting the following error:

Failed to create target binding for binding BackgroundColor for MyBooleanValue


The full error trace is as follows:

MvxBind:Error: 8.58 Problem seen during binding execution for binding BackgroundColor for MyBooleanValue - problem InvalidCastException: Cannot cast from source type to destination type.
03-05 14:18:46.434 I/mono-stdout(16474): MvxBind:Error: 8.58 Problem seen during binding execution for binding BackgroundColor for MyBooleanValue - problem InvalidCastException: Cannot cast from source type to destination type.
03-05 14:18:46.434 I/mono-stdout(16474): at Cirrious.MvvmCross.Plugins.Color.Droid.BindingTargets.MvxViewBackgroundColorBinding.SetValueImpl (System.Object target, System.Object value) [0x00000] in <filename unknown>:0
at Cirrious.MvvmCross.Plugins.Color.Droid.BindingTargets.MvxViewBackgroundColorBinding.SetValueImpl (System.Object target, System.Object value) [0x00000] in <filename unknown>:0
at Cirrious.MvvmCross.Binding.Bindings.Target.MvxConvertingTargetBinding.SetValue (System.Object value) [0x00000] in <filename unknown>:0
03-05 14:18:46.434 I/mono-stdout(16474): at Cirrious.MvvmCross.Binding.Bindings.Target.MvxConvertingTargetBinding.SetValue (System.Object value) [0x00000] in <filename unknown>:0
at Cirrious.MvvmCross.Binding.Bindings.MvxFullBinding.UpdateTargetFromSource (System.Object value) [0x00000] in <filename unknown>:0


So, I'm honestly not sure where to go from here. Is what I'm attempting possible? Am I using the right MvvmCross converter? Any pointers would be much appreciated.




Update:

Changing the converter to:

public class BackgroundColorValueConverter : MvxColorValueConverter
{
private static readonly MvxColor TrueBGColor = new MvxColor(0xDB, 0xFF, 0xCE);
private static readonly MvxColor FalseBGColor = new MvxColor(0xD6, 0xF6, 0xFF);

protected override MvxColor Convert(object value, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value ? TrueBGColor : FalseBGColor;
}
}


... resolved my problem. I also had
TextColor MyBooleanValue, Converter=TextColor
on my
LinearLayout
, which functioned similarly to
BackgroundColorValueConverter
, and I was getting the same error about failing to create target bindings.

Once I changed my AXML to read:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="BackgroundColor MyBooleanValue, Converter=BackgroundColor">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18dp"
local:MvxBind="Text MyText; TextColor MyBooleanValue, Converter=TextColor" />
</LinearLayout>


... everything worked as intended. For anyone who happens to stumble across this in the future: don't try to bind
TextColor
on a
LinearLayout
, because it doesn't work like that!

Answer

There's a working sample of BackgroundColor binding in https://github.com/MvvmCross/MvvmCross-Tutorials/blob/master/ValueConversion/ValueConversion.UI.Droid/Resources/Layout/View_Colors.axml

This uses the BackgroundColor binding from https://github.com/MvvmCross/MvvmCross/blob/v3.1/Plugins/Cirrious/Color/Cirrious.MvvmCross.Plugins.Color.Droid/BindingTargets/MvxViewBackgroundColorBinding.cs

Does this sample work for you?

If yes, can you spot the difference between that sample and the one you are using? Is it some problem with the Color plugin? (Is that loaded in your UI project?) Is it an issue with LinearLayout versus TextView? Is there any more error trace you can provide? The one line of trace you have provided is created on https://github.com/MvvmCross/MvvmCross/blob/1ec7bc5f0307595c7ae11f56727dd0e9d2a2262f/Cirrious/Cirrious.MvvmCross.Binding/Bindings/MvxFullBinding.cs#L139 - but there's normally other trace before that line.

If no, then that's worrying as it means it's a general bug...


Update: (after more info provided)

I think the problem is in your ValueConverter - to work with Android, your ValueConverter has to end up with a Native type - not the platform-independent MvxColor. The error you are seeing is an invalid cast exception - because the binding is trying to cast your MvxColor to an Android.Graphics.Color in https://github.com/MvvmCross/MvvmCross/blob/v3.1/Plugins/Cirrious/Color/Cirrious.MvvmCross.Plugins.Color.Droid/BindingTargets/MvxViewBackgroundColorBinding.cs#L25

To convert to Native, you can use the MvxColorValueConverter base class - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Plugins/Cirrious/Color/Cirrious.MvvmCross.Plugins.Color/MvxColorValueConverter.cs

One example of this is https://github.com/MvvmCross/MvvmCross-Tutorials/blob/master/ValueConversion/ValueConversion.Core/Converters/Converters.cs#L35

public class ContrastColorConverter : MvxColorValueConverter
{
    protected override MvxColor Convert(object value, object parameter, CultureInfo culture)
    {
        var input = (MvxColor) value;
        var brightnessToUse = SimpleContrast(input.R, input.G, input.B);
        return new MvxColor(brightnessToUse, brightnessToUse, brightnessToUse);
    }

    private static int SimpleContrast(params int[] value)
    {
        // this is only a very simple contrast method
        // for more advanced methods you need to look at HSV-type approaches

        int max = 0;
        foreach (var v in value)
        {
            if (v > max)
                max = v;
        }

        return 255 - max;
    }
}

There are some docs on Color Converters in https://github.com/MvvmCross/MvvmCross/wiki/Value-Converters#wiki-the-mvx-color-valueconverters