Home > Software engineering >  How to change DataGrid (C#, WPF) cell colour in the code behind (programmatically)
How to change DataGrid (C#, WPF) cell colour in the code behind (programmatically)

Time:12-17

I have a DataGrid and the columns and rows are added in the code behind.

MainWindow.xaml:

<DataGrid Name="dgResult" ItemsSource="{Binding}" AutoGenerateColumns="True" CanUserAddRows="False" ColumnWidth="*"/>

MainWindow.xaml.cs:

private void AddItemsToDataGrid()
        {
            List<Measurement> measurements = new List<Measurement>()
            {
                new Measurement(){ Address="1", Pressure="10", ErrorTemps=new List<ErrorTemp>()
                {
                    new ErrorTemp(){Temp="10", Error="0.1", IsErrorOK =true}, new ErrorTemp() { Temp = "20", Error = "0.2", IsErrorOK =true }}
                },
                new Measurement(){ Address="1", Pressure="20", ErrorTemps=new List<ErrorTemp>()
                {
                    new ErrorTemp(){Temp="10", Error="0.2", IsErrorOK =true}, new ErrorTemp() { Temp = "20", Error = "0.3", IsErrorOK =true }}
                },
                new Measurement(){ Address="1", Pressure="30", ErrorTemps=new List<ErrorTemp>()
                {
                    new ErrorTemp(){Temp="10", Error="0.34", IsErrorOK =true}, new ErrorTemp() { Temp = "20", Error = "0.5", IsErrorOK =false }}
                }
            };

            DataTable dataTable = new DataTable();
            dataTable.Columns.Add(nameof(Measurement.Address));
            dataTable.Columns.Add(nameof(Measurement.Pressure));
            foreach(ErrorTemp error in measurements[0].ErrorTemps)
                dataTable.Columns.Add(nameof(error.Error) "(" error.Temp  ")");

            List<string> row;
            foreach (Measurement measurement in measurements)
            {
                row = new List<string>() { measurement.Address, measurement.Pressure };
                foreach (ErrorTemp error in measurement.ErrorTemps)
                    row.Add(error.Error);
                dataTable.Rows.Add(row.ToArray());
            }
            dgResult.ItemsSource = dataTable.DefaultView;//bez default view jest źle
        }

Measurement.cs:

namespace DataGridCodeBehind
{
    public class Measurement
    {
        public string Address { get; set; }
        public string Pressure { get; set; }
        public List<ErrorTemp> ErrorTemps { get; set; }
    }
    public class ErrorTemp
    {
        public string Temp { get; set; }
        public string Error { get; set; }
        public bool IsErrorOK { get; set; }
    }
}

I want to change the cell background color depending on IsErrorOK value. How can I do it in the code behind?

CodePudding user response:

if the maximum amount of ErrorTemps is known beforehand then skip the datatable and use the measurements directly

    <DataGrid ItemsSource="{Binding Measurements}" AutoGenerateColumns="False" CanUserAddRows="False" ColumnWidth="*">
        <DataGrid.Resources>
            <DataTemplate x:Key="ErrorTempTemplate" DataType="{x:Type local:ErrorTemp}">
                <TextBlock x:Name="txtError" Text="{Binding Error}" ToolTip="{Binding Temp}"/>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsErrorOK}" Value="false">
                        <Setter TargetName="txtError" Property="Background" Value="Red"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Address" Binding="{Binding Address}"/>
            <DataGridTextColumn Header="Pressure" Binding="{Binding Pressure}"/>
            <DataGridTemplateColumn Header="Temp1">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ContentControl Content="{Binding ErrorTemps[0]}" ContentTemplate="{StaticResource ErrorTempTemplate}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn Header="Temp2">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ContentControl Content="{Binding ErrorTemps[1]}" ContentTemplate="{StaticResource ErrorTempTemplate}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

if it has to be completely dynamic here a little messy solution

    <DataGrid x:Name="blubb" ItemsSource="{Binding Measurements}" AutoGenerateColumns="True" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" AutoGeneratedColumns="DataGrid_AutoGeneratedColumns" CanUserAddRows="False" ColumnWidth="*">
        <DataGrid.Resources>
            <Style x:Key="ErrorTempstyle" TargetType="DataGridCell">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Tag.IsErrorOK, RelativeSource={RelativeSource Self}}" Value="false">
                        <Setter Property="Background" Value="Red"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </DataGrid.Resources>
    </DataGrid>
        private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            e.Cancel = e.PropertyName == nameof(Measurement.ErrorTemps);
        }

        private void DataGrid_AutoGeneratedColumns(object sender, System.EventArgs e)
        {
            var datagrid = (DataGrid)sender;
            var firstItem = (Measurement)datagrid.Items[0];
            for (int i = 0; i < firstItem.ErrorTemps.Count; i  )
            {
                var s = new Style { TargetType = typeof(DataGridCell), BasedOn = (Style)datagrid.Resources["ErrorTempstyle"], Setters = { new Setter(TagProperty, new Binding($"ErrorTemps[{i}]")) } };
                datagrid.Columns.Add(new DataGridTextColumn { Header = firstItem.ErrorTemps[i].Temp, CellStyle = s, Binding = new Binding($"ErrorTemps[{i}].Error") });
            }
        }

CodePudding user response:

Second solution works fine if I generate Measurments in MainViewModel constructor:

public MainViewModel()
    {
        OpenCommand = new DelegateCommand(OpenHandler);
        GenerateList();
    }

But if I will insert button, after pressing it I select text files and generate Measurements. I use button Command to do it.:

MainViewModel.cs:

 public class MainViewModel : BindableBase
{
    public ObservableCollection<Measurement> Measurements { get; set; } //= new ObservableCollection<Measurement>();
    public DelegateCommand OpenCommand { get; set; }
    public MainViewModel()
    {
        OpenCommand = new DelegateCommand(OpenHandler);
    }
    private void OpenHandler()
    {
        GenerateList();
    }
    private void GenerateList()
    {
        Measurements = new ObservableCollection<Measurement>()
        {
            new Measurement(){ Address="1", Pressure="10", ErrorTemps=new List<ErrorTemp>()
            {
                new ErrorTemp(){Temp="10", Error="0.1", IsErrorOK =true}, new ErrorTemp() { Temp = "20", Error = "0.2", IsErrorOK =true }, new ErrorTemp() { Temp = "30", Error = "0.41", IsErrorOK =true }}
            },
            new Measurement(){ Address="1", Pressure="20", ErrorTemps=new List<ErrorTemp>()
            {
                new ErrorTemp(){Temp="10", Error="0.2", IsErrorOK =true}, new ErrorTemp() { Temp = "20", Error = "0.3", IsErrorOK =true }, new ErrorTemp() { Temp = "30", Error = "0.42", IsErrorOK =true }}
            },
            new Measurement(){ Address="1", Pressure="30", ErrorTemps=new List<ErrorTemp>()
            {
                new ErrorTemp(){Temp="10", Error="0.34", IsErrorOK =true}, new ErrorTemp() { Temp = "20", Error = "0.5", IsErrorOK =false }, new ErrorTemp() { Temp = "30", Error = "0.43", IsErrorOK =false }}
            }
        };
    }

In the above example, I simplified code and generate Measurmenst list without selecting files. Now I have empty DataGrid. How to solve that?

  • Related