Swen22
Swen22

Reputation: 41

Changing color of a DataGridView Column according to value

I'm trying to change back color of a Column in my DataGridView, according to its value. Here is my code to do it. However, I get error in my if statement.

I want to change the background color of the Column to Color.Red if its value is less than zero.

private void DataColor()
{
    foreach (DataGridViewColumn col in MyDataGrid.Columns)
    if (Convert.ToInt32(col[5].Value) < 0)
    {
        MessageBox.Show("Test");

    }
}

The output I need:

Output

Upvotes: 3

Views: 2470

Answers (2)

Martin Backasch
Martin Backasch

Reputation: 1899

Looking at your wanted output:

You did not want to change the background color of your whole column, instead you want to change the color of the cell, which contains a value lower then zero.


So at first you have to get your cells. This could be done by iterating over all rows (MyDataGrid.Rows). Each row is from type the DataGridViewRow and therefore it is possible to access each cell by the column position - row.Cells[i]. Each cell is from the type DataGridViewCell. And there you can check the Value property of the cell.

A more dynamic way is iterating over all cells (row.Cells) of all rows and checking the Header of the cell.OwningColumn. If it is equals to your wanted column header, you can then check the value of the cell. If it meets your criteria you can start coloring the background color by addressing the underlying style -e e.g. cell.Style.BackColor = Color.Red;.


This could be achieved using the following example:

public partial class Form1 : Form
{
    private readonly DataGridView _myGrid;

    public Form1()
    {
        InitializeComponent();
        this.Load += this.Form1_Load;

        _myGrid = new DataGridView
        {
            DataSource = new[]
            {
                new {Name = "ABC", Amount = 2}, new {Name = "DEF", Amount = 3}, new {Name = "XYZ", Amount = -2}
            }
        };

        this.Controls.Add(_myGrid);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        foreach (DataGridViewRow row in _myGrid.Rows)
        {
            var cell = row.Cells[1];
            if (cell.Value is int val && val < 0)
            {
                cell.Style.BackColor = Color.Red;
            }          

            // A little bit more secure, if you are not sure which index the column has.
            /*
            foreach (DataGridViewCell cell in row.Cells)
            {
                if (string.Equals(cell.OwningColumn.HeaderText, "Amount"))
                {
                    if (cell.Value is int val && val < 0)
                    {
                        cell.Style.BackColor = Color.Red;
                    }
                }
            }
            */
        }
    }
}

This leads to the following output.

Output|Result


Addressing your comment:

I'm using version 1.0 [...] But there is a problem, the datasource of my datagrid is from sql server. So the program crashes if there is any null value on the column. How can i avoid it from being crashed?

Instead of pattern matching you can use int val = Convert.ToInt32(cell.Value);. To avoid the System.NullReferenceExeption on null values you can add a null check. If you receiving a null value from database, it is possible that is a DBNull.Value, which is not equal to null, so you may add a check for DBNull.

if (cell.Value == null || DBNull.Value.Equals(cell.Value))
{
    continue;
}

int val = Convert.ToInt32(cell.Value);
if (val < 0)
{
    cell.Style.BackColor = Color.Red;
}

To avoid a System.FormatException, if your column can contain something others than integers, you can use Int32.TryParse instead of Convert.ToInt32() - also with a null check.

 int val;
 if (Int32.TryParse(cell.Value.ToString(), out val))
 {
     if (val < 0)
     {
         cell.Style.BackColor = Color.Red;
     }
 }

But be carfeful: While Convert.ToInt32(cell.Value) will also convert decimal numbers to an int, Int32.TryParse(cell.Value.ToString(), out val) will return false.


Site Note: There are smarter solutions out there, but this was the first, which came to my mind.

For example:
As Jimi suggested in the comment: "Use the CellFormatting event to format a Cell"

Upvotes: 1

Karen Payne
Karen Payne

Reputation: 5157

See if the following works for you done with .NET Framework 4.8. Add a DataGridView to a new form, add the code below, changing the name of the form to the name of your form.

Full code

enter image description here

using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

namespace DataGridViewCellFormatting1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Shown += OnShown;
        }

        private void OnShown(object sender, EventArgs e)
        {
            dataGridView1.DataSource = MockedDataTable.Table();
            dataGridView1.CellFormatting += DataGridView1OnCellFormatting;
        }

        private const string _amountColumnName = "Amount";
        private void DataGridView1OnCellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (!dataGridView1.Columns[e.ColumnIndex].Name.Equals(_amountColumnName)) return;
            if (e.Value == null || !int.TryParse(e.Value.ToString(), out var amount)) return;
            
            dataGridView1.Rows[e.RowIndex].Cells[_amountColumnName].Style.BackColor = amount >0 ? 
                Color.Empty : 
                Color.Red;
        }
    }

    public class MockedDataTable
    {
        public static DataTable Table()
        {
            var dt = new DataTable();

            dt.Columns.Add(new DataColumn()
            {
                ColumnName = "id", 
                DataType = typeof(int), 
                AutoIncrement = true, 
                AutoIncrementSeed = 1
            });
            
            dt.Columns.Add(new DataColumn() { ColumnName = "Amount", DataType = typeof(int) });

            dt.Rows.Add(null, 200);
            dt.Rows.Add(null, 1000);
            dt.Rows.Add(null, -3200);
            dt.Rows.Add(null, -300);
            dt.Rows.Add(null, 500);


            return dt;
        }
    }
}

Upvotes: 3

Related Questions