c# - Change DataGridRow color according to min value


Keywords:c# 


Question: 

I have made a UI which contains a Button and a DataGrid. I have made a few calculations in the code that generate four lists which look like this:

var L = new List<MyDataObject>();
for (int z = 0; z < list_Exp.Count; z++)
{
    var d = new MyDataObject();

    d.AmountNeed = Math.Ceiling((goalexp - currentexp) / (list_Exp[z]));
    d.TotalLose = d.AmountNeed * (list_Amount_MadeFrom_One[z] * list_BuyPrice_MadeFrom_One[z] + list_Amount_MadeFrom_Two[z] * list_BuyPrice_MadeFrom_Two[z]);
    d.TotalGain = d.AmountNeed * list_AmountMade[z] * list_SellPrice[z];
    d.TotalCost = d.TotalGain - d.TotalLose;

    L.Add(d);
}

Once I got the lists I find the min value in a specific list:

int i = L.FindIndex(x => x.TotalCost == L.Min(y => y.TotalCost));

And add all the lists into dataGrid:

dataGrid.ItemsSource = L;

Now, I have been trying to change the color of Rows[i] to green or any other color. I have tried stuff like:

grid.Columns["NameOfColumn"].DefaultCellStyle.ForeColor = Color.Gray;

or

dataGrid.Rows[rowIndex].Cells[columnIndex].Style.BackColor = Color.Red;

and nothing works.

Thank You.


1 Answer: 

You could for example define a RowStyle and handle the Loaded event for the DataGridRow container something like this:

<DataGrid x:Name="dataGrid">
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <EventSetter Event="Loaded" Handler="RowLoaded" />
        </Style>
    </DataGrid.RowStyle>
</DataGrid>

private void RowLoaded(object sender, RoutedEventArgs e)
{
    DataGridRow dgr = sender as DataGridRow;
    MyDataObject x = dgr.DataContext as MyDataObject;
    if (x.TotalCost == dataGrid.Items.OfType<MyDataObject>().Min(y => y.TotalCost))
        dgr.Background = Brushes.Green;
}

only problem is when i scroll down (i have many rows), it colors few rows instead of finding only 1 min and then i get an error: "NullReferenceEXception was unhandled"

You could bind the Background property of the DataGridRow container to the current MyDataObject and the collection of objects and use a MultiValueConverter then:

<DataGrid x:Name="dataGrid" xmlns:local="clr-namespace:WpfApplication1">
    <DataGrid.Resources>
        <local:Converter x:Key="conv" />
    </DataGrid.Resources>
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="Background">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource conv}">
                        <Binding Path="." />
                        <Binding Path="ItemsSource" RelativeSource="{RelativeSource AncestorType=DataGrid}" />
                    </MultiBinding>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGrid.RowStyle>
</DataGrid>

namespace WpfApplication1
{
    public class Converter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            MyDataObject x = values[0] as MyDataObject;
            if(x != null)
            {
                IEnumerable<MyDataObject> collection = values[1] as IEnumerable<MyDataObject>;
                if(collection != null && x.TotalCost == collection.Min(y => y.TotalCost))
                    return System.Windows.Media.Brushes.Green;
            }

            return System.Windows.DependencyProperty.UnsetValue;
        }

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