0

I have a listview with values that are being updated constantly from a different thread. I want to change the color of the background according to the value of the item. After reading a lot I came to the following conclusions:

  • The correct way to set background color for list view item is via style selector.
  • Style selector is called only once in the initialization of the list.

How can I achieve this simple behavior?

xaml:

<Page
    x:Class="MyProject.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyProject"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <ListView ItemsSource="{x:Bind ViewModel.DataRef.Values, Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Center">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:ValWrapper">
                    <TextBlock Text="{x:Bind Val, Mode=OneWay}"/>
                </DataTemplate>
            </ListView.ItemTemplate>
            <ListView.ItemContainerStyleSelector>
                <local:CustomItemContainerStyleSelector>
                    <local:CustomItemContainerStyleSelector.Bad>
                        <Style TargetType="ListViewItem">
                            <Setter Property="Background" Value="Red"/>
                            
                        </Style>
                    </local:CustomItemContainerStyleSelector.Bad>

                    <local:CustomItemContainerStyleSelector.Good>
                        <Style TargetType="ListViewItem">
                            <Setter Property="Background" Value="Green"/>
                        </Style>
                    </local:CustomItemContainerStyleSelector.CloseToBad>
                </local:CustomItemContainerStyleSelector>
            </ListView.ItemContainerStyleSelector>
        </ListView>
    </Grid>
</Page>

cs:

public sealed partial class MainPage : Page
{
    public ViewModel ViewModel { get; set; }

    public MainPage()
    {
        this.InitializeComponent();
        this.ViewModel = new ViewModel();

    }

}

public class CustomItemContainerStyleSelector : StyleSelector
{
    public Style Bad { get; set; }
    public Style Good { get; set; }


    protected override Style SelectStyleCore(object item, DependencyObject container)
    {
        double threshold = 1;
        ValWrapper v = (ValWrapper)item;
        if (v.Val <= threshold)
        {
            return Bad;
        }
        else {
            return Good;
        }
    }
}

Whenever the data changes, "NotifyPropertyChanged" is called (implements INotifyPropertyChanged).

Michal Cohen
  • 11
  • 1
  • 2
  • use a solid brush property and bind it with `` – Mahmudul Hasan Jan 09 '21 at 11:00
  • https://stackoverflow.com/questions/33573929/uwp-binding-in-style-setter-not-working It doesn't seem to work... Do you have an example of code using this method? Thank you! – Michal Cohen Jan 09 '21 at 11:26
  • Currently, I don't. But a few years back I faced the same kind of problem and solved that in this way as far as I remember. I'll let you know if anything comes up. – Mahmudul Hasan Jan 09 '21 at 11:40

1 Answers1

0

Please check the following steps:

  1. Set a temporary variable _tempValue to record previous number.
  2. Bind the Background property to IsUpdate, the initial value is all false.
  3. If the number changes, please set IsUpdate to true, then the Background of ListViewItem turns red.

XAML:

<Page
    x:Class="Permisson.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Permisson"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.Resources>
        <local:ColorConverter x:Key="ColorConverter"/>
    </Page.Resources>
    <Grid>
        <StackPanel>
            <ListView ItemsSource="{x:Bind ViewModel.DataRef, Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Center">
                <ListView.ItemTemplate >
                    <DataTemplate x:DataType="local:ValWrapper">
                        <Grid Background="{Binding IsUpdate, Converter={StaticResource ColorConverter},Mode=OneWay}">
                            <TextBlock Text="{Binding Val, Mode=OneWay}"/>
                        </Grid>
                    </DataTemplate>

                </ListView.ItemTemplate>
             
            </ListView>
            <Button Content="ChangeNum" Click="Button_Click"/>
            <Button Content="ChangeNum2" Click="Button_Click_1"/>        
        </StackPanel>
   
    </Grid>
</Page>

Code behind:

namespace Permisson
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public ViewModel ViewModel { get; set; }

        public MainPage()
        {
            this.InitializeComponent();

            this.ViewModel = new ViewModel();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var v = ViewModel.DataRef[0];
       
            v.Val = 9;
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            var v = ViewModel.DataRef[1];         
            v.Val = 10;
        }
    }
    public class ViewModel
    {
        private ObservableCollection<ValWrapper> dataRef = new ObservableCollection<ValWrapper>()
        {
            new ValWrapper {Val=22,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false },
            new ValWrapper {Val=25,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false},
            new ValWrapper {Val=35,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false},
            new ValWrapper {Val=45,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false },
            new ValWrapper {Val=55,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false},
            new ValWrapper {Val=65,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false }

        };
        public ObservableCollection<ValWrapper> DataRef { get { return dataRef; } }

    }
    public class ColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            var color = new SolidColorBrush();
            if ((bool)value)
            {
                color.Color = Colors.Red;
            }
            else
            {
                color.Color = Colors.Green;

            }
            return color;
        }

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


    public class ValWrapper : INotifyPropertyChanged
    {
        private int val;
        private SolidColorBrush brush;
        public SolidColorBrush Brush
        {
            get { return brush; }
            set
            {
                brush = value;
                RaisePropertyChanged();
            }
        }


        private int _tempValue;
        public int Val
        {
            get { return val; }
            set
            {
               if(_tempValue != value && _tempValue != 0)
                {
                    IsUpdate = true;
                }
                val = value;
                RaisePropertyChanged();
                _tempValue = val;
            }
        }
        private bool _isUpdate;
        public bool IsUpdate
        {
            set
            {
                _isUpdate = value;
                RaisePropertyChanged();
            }
            get
            {
                return _isUpdate;

            }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged([CallerMemberName] string propertyname = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyname));

            }
        }
    }
}
YanGu
  • 3,006
  • 1
  • 3
  • 7