Set background color depending on data-bound value

13,217

Solution 1

What you probably need is a ValueConverter. What you are doing now is setting the background color to 'Maybe', 'No' or 'Yes', which clearly isn't a color.

What you need to do is convert that value to a color. You can do it like this.

Create a new class that implements the IValueConverter interface. It will probably look something like this:

public class YesNoMaybeToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
            switch(value.ToString().ToLower())
            {
                    case "yes":
                        return Color.Green;
                    case "no":
                        return Color.Red;
                    case "maybe":
                        return Color.Orange;
            }

            return Color.Gray;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
            // You probably don't need this, this is used to convert the other way around
            // so from color to yes no or maybe
            throw new NotImplementedException();
    }
}

Then add this class as a static resource to your XAML page like this:

<ContentPage.Resources>
   <!-- Add this line below -->
   <local:YesNoToBooleanConverter x:Key="YesNoMaybeToColorConverter" />
   <!-- You can remove the underneath -->
    <!--<ResourceDictionary>
        <Color x:Key="Maybe">#ffddbc21</Color>
        <Color x:Key="Yes">#3CB371</Color>
        <Color x:Key="No">#B22222</Color>
        <Color x:Key="Depends">#ffd78800</Color>
    </ResourceDictionary>-->
</ContentPage.Resources>

Now in your binding you have to tell him what converter to use. Do it like this:

<Label Text="{Binding Result}" HorizontalOptions="FillAndExpand" BackgroundColor="{Binding Result, Converter={StaticResource YesNoMaybeToColorConverter}}" />

It should now see the value in the Result field, put it through the converter you have defined and return the color that you corresponded to that value.

Solution 2

For a pure XAML way of achieving this without much overhead, you can use a DataTrigger. Note that you can add as many setters per trigger as needed, making this slightly more flexible than the previously suggested solutions, it's also keeping view logic in the view where it should be.

<Label Text="{Binding Result}" HorizontalOptions="FillAndExpand">
    <Label.Triggers>
        <DataTrigger TargetType="Label" Binding="{Binding Result}" Value="Yes">
            <Setter Property="BackgroundColor" Value="#3CB371" />
        </DataTrigger>
        <DataTrigger TargetType="Label" Binding="{Binding Result}" Value="No">
            <Setter Property="BackgroundColor" Value="#B22222" />
        </DataTrigger>
        <DataTrigger TargetType="Label" Binding="{Binding Result}" Value="Maybe">
            <Setter Property="BackgroundColor" Value="#ddbc21" />
        </DataTrigger>
        <DataTrigger TargetType="Label" Binding="{Binding Result}" Value="Depends">
            <Setter Property="BackgroundColor" Value="#d78800" />
        </DataTrigger>
    </Label.Triggers>
</Label>

Note that you could probably cut out one of the triggers by setting the BackgroundColor property to a sensible default (likely "Depends" in this case).

Solution 3

I found 2 another options for managing this, because sometimes we need to change not only color but font and other values. At first you need to add name to your control like this:

  1. You can set color and other properties in code behind like this, this way needs some time for updatig, so it's not the best choice, when Text property was just updated.

    protected override void OnAppearing()
    {
        if (this.MyLabel.Text.Contains("yes")
                 {
            this.MyLabel.TextColor = Color.FromHex("#98ee99");
        }
        else
        {
            this.MyLabel.TextColor = Color.FromHex("#ff867c");
        }
    

    }

  2. Another way is using Prism EventAggregator, I guess in Xamarin.Forms it is Messaging Center. Here is one good example with Prism [https://codesandchips.wordpress.com/2017/03/30/xamarin-getting-started-with-prism-messaging/][1]

In this case you should send Event with your value from whatever place in your project, for example when it's should be already updated.

 _evenAggregator.GetEvent<MyEvent>().Publish(Result);

Then you should subscribe the Event in place where you need to receive fresh value. In this case it should be OnAppearing method in code behind class.

 void UpdateColor(string result)
    {
     if(result.Contains("yes")
            {
            this.MyLabel.TextColor = Color.FromHex("#98ee99");
            }
            else
            {
            this.MyLabel.TextColor = Color.FromHex("#ff867c");
            }
    }





     protected override void OnAppearing()
        {
base.OnAppearing();
      _eventAggregator.GetEvent<MyEvent>().Subscribe(UpdateColor);
        }
Share:
13,217
alt-ctrl-dev
Author by

alt-ctrl-dev

Constantly learning from and helping the dev community.

Updated on July 24, 2022

Comments

  • alt-ctrl-dev
    alt-ctrl-dev almost 2 years

    I've seen some answers before but nothing really helped me out.

    I also have a class DecideModel (This will be a dataset retrieved from DB, but for purpose of this question, I have added an ObservableCollection) which contains

    static DecideModel()
        {
            All = new ObservableCollection<DecideModel>
            {
                new DecideModel
                {
                    DatePerformed = new DateTime(2015, 4, 06),
                    Result = "Maybe"
                },
                new DecideModel
                {
                    DatePerformed = new DateTime(2015, 4, 05),
                    Result = "No"
                },
                new DecideModel
                {
                    DatePerformed = new DateTime(2015, 4, 04),
                    Result = "Yes"
                }
            };
        }
    
        public DateTime DatePerformed { set; get; }
    
        public string  Result { set; get; }
    
        public static IList<DecideModel> All { set; get; }
    }
    

    In my XAML code I have

    <ContentPage.Resources>
        <ResourceDictionary>
            <Color x:Key="Maybe">#ffddbc21</Color>
            <Color x:Key="Yes">#3CB371</Color>
            <Color x:Key="No">#B22222</Color>
            <Color x:Key="Depends">#ffd78800</Color>
        </ResourceDictionary>
    </ContentPage.Resources>
    
    <Label Text="{Binding Result}" HorizontalOptions="FillAndExpand" BackgroundColor="{StaticResource {BindingSource Result}}" />
    

    I am trying to dynamically set the background color of the label with respect to what result I have obtained from the Object.

    Please let me know if you have any idea on how to do it. I am looking for any useful option available.

  • Cobysan
    Cobysan over 2 years
    This helped me. I like this item for a quick solution for simple situations. Thanks.