ListView Bindings Issue

Hello, I am having a little issue using List View in Xamarin Forms.

I have populated my ListView using the MVVM Architecture. The codes are as follows :smile:
What I want to achieve is to have my TextColor Attribute turn Red for one of my Label in the textview.. How can I achieve this correctly ?
**My XAML Page : **

 <ListView.ItemTemplate>
   <DataTemplate>
     <ViewCell>
       <Grid Padding="12">
          <Label Text="{Binding UID}" TextColor="Black" FontSize="12"/>
          <Label Text="{Binding battery}" TextColor="{Binding StatusColor}" FontSize="12" XAlign="End"/>
       </Grid>
     </ViewCell>
   </DataTemplate>
 </ListView.ItemTemplate>

</ListView>

**My TagsViewModel.cs (MVVM) : **

public class TagsViewModel : INotifyPropertyChanged
{
private List _tagsModel;

    public List<Tags> TagsModel
    {
        get { return _tagsModel; }
        set
        {
            _tagsModel = value;
            OnPropertyChanged();
        }
    }

    private string _statusColor;

    public string StatusColor
    {
        get { return _statusColor; }
        set
        {
            _statusColor = value;
            OnPropertyChanged();
        }
    }

    public TagsViewModel()
    {
        TagsModel = new List<Tags>();

        TagsModel.Add(new Tags() { id = 1, UID = "324324", UIDid = 4324325, battery = "14/02/2017" });
        TagsModel.Add(new Tags() { id = 2, UID = "258225", UIDid = 4324325, battery = "15/02/2017" });
        TagsModel.Add(new Tags() { id = 3, UID = "854224", UIDid = 4324325, battery = "16/02/2017" });
        TagsModel.Add(new Tags() { id = 4, UID = "582253", UIDid = 4324325, battery = "14/02/2017" });
        TagsModel.Add(new Tags() { id = 5, UID = "253245", UIDid = 4324325, battery = "17/02/2017" });

        StatusColor = "Red";



    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Best Answer

  • NMackayNMackay GB mod
    edited February 2017 Accepted Answer

    @HamsheedSalamut

    Noticed an error which @AlessandroCaliaro correctly spotted, I missed his post

    <Label Text="{Binding battery}" TextColor="{Binding UID, Converter={converters:StartsWithToColorConverter }}" FontSize="12" XAlign="End"/>

    I'm pretty busy so trying to answer you and write some code at the same time :smile: don't do multi-tasking

Answers

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    StatusColor property should be of type "Color", not a string.

  • @AlessandroCaliaro said:
    StatusColor property should be of type "Color", not a string.

    But when used in the BindingContext, it can be treated as a string also.. I have test the Binding outside the listview in a label and it works. The problem arises when I bind it in the listview.

  • NMackayNMackay GBInsider, University mod

    @HamsheedSalamut

    Best way would be a data trigger or a value converter rather than setting presentation logic in the viewmodel

    https://blog.xamarin.com/triggers-in-xamarin-forms/

  • @NMackay said:
    @HamsheedSalamut

    Best way would be a data trigger or a value converter rather than setting presentation logic in the viewmodel

    https://blog.xamarin.com/triggers-in-xamarin-forms/

    I want to set the TextColor to Red of each UID that starts with the digit '3'. How can I achieve this ?

  • voidstreamvoidstream FRMember ✭✭✭
    edited February 2017

    The best way (my point of view) is to use datatriggers like this:

    <Label Text="{Binding battery}" FontSize="12" XAlign="End">
        <Label.Triggers>    
            <DataTrigger TargetType="Label" Binding="{Binding CodeStatus}" Value="0">
                <Setter Property="TextColor" Value="White"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <DataTrigger TargetType="Label" Binding="{Binding CodeStatus}" Value="1">
                <Setter Property="TextColor" Value="Aqua"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <!-- You can add more datatrigger here -->
        </Label.Triggers>
    </label>
    

    The color value should not be in the class (it's not MVVM), the designer must bind the codestatus and choose color.

  • NMackayNMackay GBInsider, University mod
    edited February 2017

    @HamsheedSalamut

    using System;
    using System.Globalization;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace Foobar.Converters
    {
        public class StartsWithToColorConverter : IValueConverter, IMarkupExtension
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if (Equals(value,null)) return "#000000";
                var valueAsString = value.ToString();
                return valueAsString.StartsWith("3") ? "#FF0000" : "#000000";
    
            }
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException("Only one way bindings are supported with this converter");
            }
    
            public object ProvideValue(IServiceProvider serviceProvider)
            {
                return this;
            }
        }
    }
    
  • NMackayNMackay GBInsider, University mod
    edited February 2017

    Then in your marksup

    <Label Text="{Binding battery}" TextColor="{Binding UID, Converter={converters:StartsWithToColorConverter }}" FontSize="12" XAlign="End"/>
    

    Just add the converter namespace in the xaml namespaces

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @NMackay

    I think you should verify value before use it

    if(value != null && value is string){
    
            var valueAsString = value.ToString();
            return valueAsString.StartsWith("3") ? "#FF0000" : "#000000";
    
    }
    else
        return "#000000";
    

    But I think you should return a Color, not a string...

  • voidstreamvoidstream FRMember ✭✭✭

    Or you can use IMarkupExtensions too ;) But i prefer DataTriggers :smile:

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @NMackay said:
    Then in your marksup

    <Label Text="{Binding battery}" TextColor="{Binding StatusColor, Converter={converters:StartsWithToColorConverter }}" FontSize="12" XAlign="End"/>
    

    Just add the converter namespace in the xaml namespaces

    TextColor = "{Binding UID....

  • NMackayNMackay GBInsider, University mod

    @AlessandroCaliaro

    I did test and it works fine, your spot on about the null check, I have changed the sample

  • @TonyPinot said:
    The best way (my point of view) is to use datatriggers like this:

    The color value should not be in the class (it's not MVVM), the designer must bind the codestatus and choose color.

    "The color value should not be in the class (it's not MVVM), the designer must bind the codestatus and choose color."

    What do you mean by this ? Where the Value property should be specified if not in the MVVM >

  • voidstreamvoidstream FRMember ✭✭✭

    @NMackay said:
    Then in your marksup

    <Label Text="{Binding battery}" TextColor="{Binding StatusColor, Converter={converters:StartsWithToColorConverter }}" FontSize="12" XAlign="End"/>
    

    Just add the converter namespace in the xaml namespaces

    I think if he used Markup he should bind the CodeStatus, not the statusColor and the markup return the color

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            switch((int)value)
            {
                case 0: return Color.Aqua;
                case 1: return Color.Red;
                case 2: return Color.Green;
                default: return Color.White;
            }
        }
    
  • NMackayNMackay GBInsider, University mod

    @TonyPinot

    I just implemented IMarkupExtension so you don't have to add the converter as resource, makes it easier to get the converter working which can be tricky if it's your 1st time, small cost of IServiceProvider I guess.

  • @NMackay said:
    Then in your marksup

    <Label Text="{Binding battery}" TextColor="{Binding StatusColor, Converter={converters:StartsWithToColorConverter }}" FontSize="12" XAlign="End"/>
    

    Just add the converter namespace in the xaml namespaces

    Can you provide the namespace as well ?

  • NMackayNMackay GBInsider, University mod
    edited February 2017

    @HamsheedSalamut

    It's where the converter lives in your assembly

    xmlns:converters="clr-namespace:MyAppName.Converters;assembly=MyAppName"

  • voidstreamvoidstream FRMember ✭✭✭
    edited February 2017

    @HamsheedSalamut said:

    @TonyPinot said:
    The best way (my point of view) is to use datatriggers like this:

    <Label Text="{Binding battery}" FontSize="12" XAlign="End">
        <Label.Triggers>    
            <DataTrigger TargetType="Label" Binding="{Binding CodeStatus}" Value="0">
                <Setter Property="TextColor" Value="White"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <DataTrigger TargetType="Label" Binding="{Binding CodeStatus}" Value="1">
                <Setter Property="TextColor" Value="Aqua"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <!-- You can add more datatrigger here -->
        </Label.Triggers>
    </label>
    

    The color value should not be in the class (it's not MVVM), the designer must bind the codestatus and choose color.

    "The color value should not be in the class (it's not MVVM), the designer must bind the codestatus and choose color."

    What do you mean by this ? Where the Value property should be specified if not in the MVVM >

    MVVM concept is split the design and the data and if you add the color (design value) in the model class you didn't respect this (my point of view in this situation).

    Now, you got a StatusColor right? So you must have a CodeStatut right? So bind the codestatus and edit color in XAML by datatriggers or markup

    I'm a newbie in MVVM but i think we should works like this

  • voidstreamvoidstream FRMember ✭✭✭
    edited February 2017

    @NMackay said:
    @HamsheedSalamut

    It's where the converter lives in your assembly

    xmlns:converters="clr-namespace:MyAppName.Converters;assembly=MyAppName"

    If you are in the same solution you just can write here:

    xmlns:converters="clr-namespace:MyAppName.Converters"`
    

    Tips:

    • write xmlns:converters="
    • start to write converters and push TAB
    • autocomplete <3
  • NMackayNMackay GBInsider, University mod
    edited February 2017

    @TonyPinot

    I think your trigger solution is the nicest, I don't think the vanilla triggers can handle conditions such as starts with (please correct me if I'm wrong).

    I use the awesome little library that will handle this scenario purely in XAML.

    http://www.davidbritch.com/2016/04/xamarinforms-behaviors_13.html
    https://www.nuget.org/packages/Behaviors.Forms/

    edit:

    tell a lie, it doesn't support starts with.

  • voidstreamvoidstream FRMember ✭✭✭

    @NMackay

    I don't know, i'm a newbie i need to learn more with XAML, but i think datatriggers is the most natural writing.
    For example, i never try to use behaviors or effect, but i will ;)

  • NMackayNMackay GBInsider, University mod

    @TonyPinot

    Everyday is a school day working with Xamarin and Xamarin Forms :wink:

  • @TonyPinot said:
    @NMackay

    I don't know, i'm a newbie i need to learn more with XAML, but i think datatriggers is the most natural writing.
    For example, i never try to use behaviors or effect, but i will ;)

    The CodeStatus, where do I need to set it ? Should I define a property in the MVVM class ? or define it a model? I don't understand this part Value="1"

  • NMackayNMackay GBInsider, University mod

    @HamsheedSalamut

    Does the value converter approach work?

  • @NMackay said:
    @HamsheedSalamut

    Does the value converter approach work?

    Nope. Its not working :(

  • NMackayNMackay GBInsider, University mod
    edited February 2017 Accepted Answer

    @HamsheedSalamut

    Noticed an error which @AlessandroCaliaro correctly spotted, I missed his post

    <Label Text="{Binding battery}" TextColor="{Binding UID, Converter={converters:StartsWithToColorConverter }}" FontSize="12" XAlign="End"/>

    I'm pretty busy so trying to answer you and write some code at the same time :smile: don't do multi-tasking

  • @NMackay said:
    @HamsheedSalamut

    Noticed an error which @AlessandroCaliaro correctly spotted, I missed his post

    <Label Text="{Binding battery}" TextColor="{Binding UID, Converter={converters:StartsWithToColorConverter }}" FontSize="12" XAlign="End"/>

    I'm pretty busy so trying to answer you and write some code at the same time :smile: don't do multi-tasking

    Thanks ! It works like charm _ :)

    Thank you very much :)

  • NMackayNMackay GBInsider, University mod

    @HamsheedSalamut

    Your welcome.

  • @TonyPinot said:
    @NMackay

    I don't know, i'm a newbie i need to learn more with XAML, but i think datatriggers is the most natural writing.
    For example, i never try to use behaviors or effect, but i will ;)

    I would like to certainly try your solution and make it work. Please help me to proceed :)

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @NMackay said:
    @TonyPinot

    Everyday is a school day working with Xamarin and Xamarin Forms :wink:

    I will always remain a donkey

  • NMackayNMackay GBInsider, University mod

    @HamsheedSalamut

    I'm not sure you can get round your scenario using startswith without using a value converter somewhere, the library I mentioned allows more powerful comparisons for triggers but it doesn't support startswith. If your trigger is based on a boolean you'd have to do something like the following (I don't know if this would work)

    <Label Text="{Binding battery}" FontSize="12" XAlign="End"> <Label.Triggers> <DataTrigger TargetType="Label" Binding="{Binding UID, Converter={converters:StartsWithToBoolConverter }}" Value="0"> <Setter Property="TextColor" Value="Black"/> <!-- You can add more properties here --> </DataTrigger> <DataTrigger TargetType="Label" Binding="{Binding UID, Converter={converters:StartsWithToBoolConverter }}" Value="1"> <Setter Property="TextColor" Value="Red"/> <!-- You can add more properties here --> </DataTrigger> <!-- You can add more datatrigger here --> </Label.Triggers> </label>

    Converter

    using System;
    using System.Globalization;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace Foobar.Converters
    {
        public class converters:StartsWithToBoolConverter : IValueConverter, IMarkupExtension
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if (Equals(value,null)) return False;
                var valueAsString = value.ToString();
                return valueAsString.StartsWith("3") ? True : False;
    
            }
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException("Only one way bindings are supported with this converter");
            }
    
            public object ProvideValue(IServiceProvider serviceProvider)
            {
                return this;
            }
        }
    }
    

    I haven't tested but maybe worth trying if you have spare time.

  • voidstreamvoidstream FRMember ✭✭✭
    edited February 2017

    @HamsheedSalamut said:

    @TonyPinot said:
    @NMackay

    I don't know, i'm a newbie i need to learn more with XAML, but i think datatriggers is the most natural writing.
    For example, i never try to use behaviors or effect, but i will ;)

    I would like to certainly try your solution and make it work. Please help me to proceed :)

    It's easier, Value="" match with the _value _of Binding=""

    Binding="{Binding CodeStatus}" Value="0">
    

    For example

    <Label Text="{Binding battery}" FontSize="12" XAlign="End">
        <Label.Triggers>    
            <DataTrigger TargetType="Label" Binding="{Binding battery}" Value="14/02/2017">
                <Setter Property="TextColor" Value="White"/>
                <Setter Property="BackgroundColor" Value="Red"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <DataTrigger TargetType="Label" Binding="{Binding battery}" Value="15/02/2017">
                <Setter Property="TextColor" Value="Aqua"/>
                <Setter Property="BackgroundColor" Value="White"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <DataTrigger TargetType="Label" Binding="{Binding battery}" Value="16/02/2017">
                <Setter Property="TextColor" Value="White"/>
                <Setter Property="BackgroundColor" Value="Green"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <DataTrigger TargetType="Label" Binding="{Binding battery}" Value="14/02/2017">
                <Setter Property="TextColor" Value="Black"/>
                <Setter Property="BackgroundColor" Value="Yellow"/>
                <!-- You can add more properties here -->
            </DataTrigger>  
            <!-- You can add more datatrigger here -->
        </Label.Triggers>
    </label>
    
  • NMackayNMackay GBInsider, University mod

    @TonyPinot

    So how do you handle the startsWith scenario?

  • voidstreamvoidstream FRMember ✭✭✭
    edited February 2017

    @NMackay

    Code:

       private int _codeStatus;
    
        public int CodeStatus
        {
            get { return _codeStatus; }
            set
            {
                _codeStatus = value;
                OnPropertyChanged();
            }
        }
    
        public TagsViewModel()
        {
            TagsModel = new List<Tags>();
    
            TagsModel.Add(new Tags() { id = 1, UID = "324324", UIDid = 4324325, battery = "14/02/2017" });
            TagsModel.Add(new Tags() { id = 2, UID = "258225", UIDid = 4324325, battery = "15/02/2017" });
            TagsModel.Add(new Tags() { id = 3, UID = "854224", UIDid = 4324325, battery = "16/02/2017" });
            TagsModel.Add(new Tags() { id = 4, UID = "582253", UIDid = 4324325, battery = "14/02/2017" });
            TagsModel.Add(new Tags() { id = 5, UID = "253245", UIDid = 4324325, battery = "17/02/2017" });
    
            CodeStatus = 0;
        }
    

    In XAML:

    <Label Text="{Binding battery}" FontSize="12" XAlign="End">
        <Label.Triggers>    
            <DataTrigger TargetType="Label" Binding="{Binding CodeStatus}" Value="0">
                <Setter Property="TextColor" Value="White"/>
                <Setter Property="BackgroundColor" Value="Red"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <DataTrigger TargetType="Label" Binding="{Binding CodeStatus}" Value="1">
                <Setter Property="TextColor" Value="Black"/>
                <Setter Property="BackgroundColor" Value="Transparent"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <!-- You can add more datatrigger here -->
        </Label.Triggers>
    </label>
    

    And if your status never change so it's not a statut?, you should use StaticResource or DynamicResource?

  • NMackayNMackay GBInsider, University mod

    @TonyPinot

    I see what your doing but I think id is just a sequence and wouldn't work in his scenario (any UID starting with 3 turns red), I totally agree though, if it's lookup values etc your approach would be the best.

  • voidstreamvoidstream FRMember ✭✭✭

    @NMackay said:
    @TonyPinot

    I see what your doing but I think id is just a sequence and wouldn't work in his scenario (any UID starting with 3 turns red), I totally agree though, if it's lookup values etc your approach would be the best.

    Like this?

    Code

    private bool _isRed;
    public bool IsRed
    {
        get { return _isRed; }
        set
        {
                _isRed = value;
                OnPropertyChanged();
        }
    }
    
    public TagsViewModel()
    {
        TagsModel = new List<Tags>();
    
        TagsModel.Add(new Tags() { id = 1, UID = "324324", UIDid = 4324325, battery = "14/02/2017" });
        TagsModel.Add(new Tags() { id = 2, UID = "258225", UIDid = 4324325, battery = "15/02/2017" });
        TagsModel.Add(new Tags() { id = 3, UID = "854224", UIDid = 4324325, battery = "16/02/2017" });
        TagsModel.Add(new Tags() { id = 4, UID = "582253", UIDid = 4324325, battery = "14/02/2017" });
        TagsModel.Add(new Tags() { id = 5, UID = "253245", UIDid = 4324325, battery = "17/02/2017" });
    
        IsRed = True;
    }
    

    XAML

    <Label Text="{Binding battery}" FontSize="12" XAlign="End">
        <Label.Triggers>    
            <DataTrigger TargetType="Label" Binding="{Binding IsRed}" Value="True">
                <Setter Property="TextColor" Value="White"/>
                <Setter Property="BackgroundColor" Value="Red"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <DataTrigger TargetType="Label" Binding="{Binding IsRed}" Value="False">
                <Setter Property="TextColor" Value="Black"/>
                <Setter Property="BackgroundColor" Value="Transparent"/>
                <!-- You can add more properties here -->
            </DataTrigger>
            <!-- You can add more datatrigger here -->
        </Label.Triggers>
    </label>    
    
  • NMackayNMackay GBInsider, University mod

    Wouldn't that just make every label red?

  • voidstreamvoidstream FRMember ✭✭✭
    edited February 2017

    @NMackay @HamsheedSalamut
    Okay my bad i didn't read the 3 digits conditions ! I was writing my first answer when he add this condition :/

    So MarkupExtension is the best way if you have only one property to change (like TextColor) but if you want edit more i think you should add in your class Tag a property with the first digit and use datatriggers after

    Or, a MarkupExtension can return a XAML Style?

Sign In or Register to comment.