Highlight cells if found

c#, datagrid, wpf

Solution

You can achieve that using `IMultiValueConverter` which will return true if Text lies within the range otherwise return false. `Based on the value returned by converter, change background color`.

Pass three parameters to converter:

- Actual Text value.

- Minimum value.

- Maximum value.

Converter

public class ItemExistInRangeConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType,
                          object parameter, CultureInfo culture)
    {
        bool itemsExistInRange = false;
        if (values.Length == 3)
        {
            int outputValue = int.MinValue;
            if (Int32.TryParse(values[0].ToString(), out outputValue))
            {
                int minValue = (int)values[1];
                int maxValue = (int)values[2];
                itemsExistInRange = minValue <= outputValue
                                     && outputValue <= maxValue;
            }
        }
        return itemsExistInRange;
    }

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

XAML

<DataGrid.Resources>
    <local:ItemExistInRangeConverter x:Key="ItemExistInRangeConverter"/>
    <sys:Int32 x:Key="MinimumValue">1</sys:Int32>
    <sys:Int32 x:Key="MaximumValue">50</sys:Int32>
    <Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
       <Style.Triggers>
          <DataTrigger Value="True">
             <DataTrigger.Binding>
                 <MultiBinding Converter="{StaticResource ItemExistInRangeConverter}">
                     <Binding Path="Text" RelativeSource="{RelativeSource Self}"/>
                     <Binding Source="{StaticResource MinimumValue}"/>
                     <Binding Source="{StaticResource MaximumValue}"/>
                 </MultiBinding>
             </DataTrigger.Binding>
             <Setter Property="Background" Value="LightGreen" />
          </DataTrigger>
        </Style.Triggers>
    </Style>
</DataGrid.Resources>

Make sure you add corresponding namespace at root level:

xmlns:local="clr-namespace:NamespaceOfConverter"
// Replace NamespaceOfConverter with namespace where converter resides.
xmlns:sys="clr-namespace:System;assembly=mscorlib"

UPDATE (In case want to find item in an array of numbers)

Assuming your array of numbers property exist in code behind or view model class.

First of all you need to `set ItemsSource of DataGrid to DataTable` instead of DataContext.

Second, `set DataContext of DataGrid to this from code behind or an instance of viewModel class`.

Also, to refresh GUI your class should implement INotifyPropertyChanged interface.

Now, assume you have a property say:

public int[] RangeNumbers { get; set; }

By default it will contain list of numbers which you want to highlight.

XAML will look like this:

<DataGrid.Resources>
    <local:ItemExistInRangeConverter x:Key="ItemExistInRangeConverter"/>
       <Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
           <Style.Triggers>
              <DataTrigger Value="True">
                 <DataTrigger.Binding>
                    <MultiBinding Converter="{StaticResource ItemExistInRangeConverter}">
                       <Binding Path="Text" RelativeSource="{RelativeSource Self}"/>
                       <Binding Path="DataContext.RangeNumbers" 
                                RelativeSource="{RelativeSource FindAncestor,
                                                  AncestorType=DataGrid}"/>
                    </MultiBinding>
                 </DataTrigger.Binding>
               <Setter Property="Background" Value="LightGreen" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</DataGrid.Resources>

Converter code:

public class ItemExistInRangeConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType,
                          object parameter, CultureInfo culture)
    {
        bool itemsExistInRange = false;
        if (values.Length == 2)
        {
            int outputValue = int.MinValue;
            int[] rangeNumbers = (int[])values[1];
            if (rangeNumbers != null && 
                Int32.TryParse(values[0].ToString(), out outputValue))
            {
                itemsExistInRange = rangeNumbers.Contains(outputValue);
            }
        }
        return itemsExistInRange;
     }

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

Now, items will be highlighted based on initial numbers in `RangeNumbers`. But suppose you update array afterwards, you need to raise property changed event so that GUI gets refreshed something like this:

RangeNumbers = new int[] { 23, 45, 47, 69 };
OnPropertyChanged("RangeNumbers");

Problem

I have a datagrid that outputs 90 rows after being bound to a datatable. The rows values are the 1-90 ascending. What I want to achieve: If the number is found in an array/range/list of numbers then highlight it green. I have managed to get it to highlight the cell based on 1 value, but I want to highlight several cells if they are found in the range. ``` <DataGrid Name="grid" ItemsSource="{Binding}" Height="300" Width="900" AutoGenerateColumns="False" VerticalScrollBarVisibility="Disabled" HorizontalAlignment="Center" VerticalAlignment="Top" RowHeight="40"> <DataGrid.Resources> <Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}"> <Style.Triggers> <Trigger Property="Text" Value="1"> <Setter Property="Background" Value="LightGreen" /> </Trigger> </Style.Triggers> </Style> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Path=Number}" ElementStyle="{StaticResource BackgroundColourStyle}" MinWidth="40"> </DataGridTextColumn> </DataGrid.Columns> <DataGrid.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Vertical" /> </ItemsPanelTemplate> </DataGrid.ItemsPanel> </DataGrid> ``` you can see I have a trigger property of the value 1. This works fine, but as above, how do I change this so if a cell is in the range set in c# in backend then highlight it green. binding to datatable: ``` calledGrid.DataContext = calledNumbers.DefaultView; ``` The actual making of the datatable: ``` DataSet dataSet = new DataSet("myDS"); this.bingoCalls(dataSet); DataTable numbersTable = new DataTable("Numbers"); numbersTable.Columns.Add("Number", typeof(Int32)); for (int i = 1; i < 91; i++) { numbersTable.Rows.Add(i); } dataSet.Tables.Add(numbersTable); ``` Thanks for any help guys. If you need any more info or I have been to vague or unclear about anything please do ask and i will do my best to respond ASAP. Also please excuse any ignorance I may have, I am very new to wpf. I will do my best.

Original source