Home > other >  Changing the back colour on a specific row of a Datagridview
Changing the back colour on a specific row of a Datagridview

Time:10-19

I tried to change the back colour with a condition as follows. It doesn't work.

private void frmJobStat_Load(object sender, EventArgs e)
{
    sql = "SELECT `jobno` AS JOB_NO,`lcode` AS LOCATION,iscompleted, iscannotrepair, isissued FROM `jobmaster`";
    config.Load_DTG(sql, dgvJobNo);

    dgvJobNo.Columns[1].Visible = false;
    //dgvJobNo.Columns[2].Visible = false;
    //dgvJobNo.Columns[3].Visible = false;
    //dgvJobNo.Columns[4].Visible = false;
    dgvJobNo.Columns[0].Width = 100;
    dgvJobNo.DefaultCellStyle.Format = "000000";

    for (int i = 0; i < dgvJobNo.Rows.Count - 1; i  )
    {
        if (Boolean.Parse(dgvJobNo.Rows[i].Cells[2].Value.ToString()))
        {
            dgvJobNo.Rows[i].DefaultCellStyle.BackColor = Color.Blue;
        }
    }

    //foreach (DataGridViewRow rw in dgvJobNo.Rows)
    //{
    //    if (rw.Cells[2].Value.ToString()=="1")
    //    {
    //        dgvJobNo.DefaultCellStyle.BackColor = Color.Red;
    //    }
    //}
}

I have load one column from a table with true/false values. I need to change the colour when it is true.

Can anyone help me?

CodePudding user response:

People who use a DataGridView quite often tend to directly fiddle with Cells and how values are displayed. It is way easier to use DataBinding: separate the data from the way that it is displayed using DataGridView.DataSource.

Use the DataSource

I haven't got the faintest Idea what you are showing in your DataGridView, so let's assume you want to show Products.

So somewhere you have a class to save and retrieve Products from some place. Of course you hide that the data is fetched from a database, and how this is fetched (DbConnection and DbCommand? Or maybe entity framework and LINQ?). All you know is that you can store Products and retrieve them later on:

interface IRepository
{
    int AddProduct(Product product)   // returns Id
    Product FetchProduct(int id);

    IEnumerable<Product> FetchProducts(...); // fetches several Products to display
    ...

Oh, before I forget:

class Product
{
    public int Id {get; set;}
    public string Name {get; set;}

    public decimal Price {get; set;}
    public int Stock {get; set;}
    ...
}

Suppose you have a form with a DataGridView where you want to show some Products. Using visual studio designer you have added a DataGridView and some columns

In the constructor:

public MyForm : ...
{
    InitializeComponent();

    this.columnId.DataPropertyName = nameof(Product.Id);
    this.columnName.DataPropertyName = nameof(Product.Name);
    this.columnPrice.DataPropertyName = nameof(Product.Price);
    ...
}

Of course you have access to the repository and a method that fetches the Products that you want to show:

private IRepository ProductRepository => ...

private IEnumerable<Product> FetchProductsToDisplay()
{
    return this.ProductRepository.FetchProducts(...);
}

Now to display the fetched Products, we use the DataSource of the DataGridView in a BindingList:

private BindingList<Product> DisplayedProducts
{
    get => (BindingList<Product>)this.dataGridView1.DataSource;
    set => this.dataGridView1.DataSource = value;
}

private void ShowInitialProducts()
{
    this.DisplayedProducts = new BindingList<Product>( this.FetchProductsToDisplay().ToList());

And of course, while Loading the form:

private void OnFormLoading(object sender, ...)
{
    this.ShowInitialProducts();
}

This is enough to show all Products. If the operator Adds or Removes some rows, or edits the values of the rows, they are automatically updated. If the operator indicates that he has finished editing the Products, for instance by clicking OK button:

private void OnButtonOkClicked(object sender, ...)
{
    this.ProcessEditedProducts();
}

private void ProcessEditedProducts()
{
    ICollection<Product> displayedProducts = this.DisplayedProducts();
    // find out which Products are added / removed / changed
    this.ProcessProducts(displayedProducts);
}

By the way, did you notice that my procedures are always very small: they do only one thing, they serve one purpose. This makes them easy to understand, good to reuse, easy to test and to change.

Intermezzo: some useful functions

The following small methods might be handy. If you have a DataGridViewRow, you can get DataGridViewRow.DataBoundItem to get the Product that is shown in the Row.

// Get the Product that is displayed in row with rowIndex
private Product DisplayedProduct(int rowIndex)
{
    return (Product)this.dataGridView1.Rows[rowIndex].DataBoundItem;
}

// Get the Currently Selected Product
private Product CurrentProduct()
{
    return (Product)this.dataGridView1.CurrentRow.DataBoundItem;
}

// Get all Selected Products (if you allow multi-selecting
private IEnumerable<Product> GetSelectedProducts()
{
    return this.dataGridView1.SelectedRows.Cast<DataGridViewRow>()
        .Select(row => row.DataBoundItem)
        .Cast<Product>();
}

Back to your question

Apparently, there is a predicate which makes that you want to give some Cells a different background Color. For example: if the stock is zero, you want it red, and if the stock is low, you want it orange. Otherwise you want a white background

Color ZeroStockColor = Color.Red;
Color LowStockColor = Color.Orange;
int LowStockValue = 10;               // 10 or less: Stock is low

Just before the value of a cell is displayed, you get the chance to format the cell, using event DataGridView.CellFormatting.

If the column that shows the Stock is being formatted, we want to check the Value of the Stock. If it is zero, we want red background, if it is low, we have an orange background.

private void OnCellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
    // e contains the rowIndex and columnIndex
    if (e.ColumnIndex = this.columnStockValue.Index)
    {
        // Formatting the cell that contains the stock value
        Product productToFormat = this.DisplayedProduct(e.RowIndex);
        DataGridViewCell cellToFormat = this.dataGridView1
            .Rows[e.RowIndex].Cells[e.ColumnIndex];

        if (productToFormat.Stock == 0)
        {
            // no stock: RED!
            cellToFormat.Style.BackColor = ZeroStockColor;
        }
        else if (productToFormat.Stock <= LowStockValue)
        {
            // Low stock: Orange!
            cellToFormat.Style.BackColor = LowStockColor;
        }
        else
        {
            // enough stock: use default cell Style of this column
            cellToFormat.Style =  null;
        }
    }
}

If a DataGridViewCell.Style is not null, then this Style is used to format the cell. if the Style is null, then DataGridViewColumn.DefaultCellStyle is used, unless that value is also null. In that case we go even higher: DataGridView.DefaultCellStyle

Conclusion

If you use DataBinding, it is easy to show your data in the DataGridView and to access the objects in the DataGridView: the current one, the selected ones or the one that is displayed in the 10th row.

To format a cell, use DataGridViewCell.Style, or the DefaultCellStyle in the column, or in the DataGridView

Use event DataGridView.CellFormatting to format the cell just before it will be displayed.

CodePudding user response:

It is not working because in C#, if you create a new object just by assignment, it will not generate child references so

 dgvJobNo.Rows[i].DefaultCellStyle.BackColor 

could be non existent here.

You have to do something like this

for (int i = 0; i < dgvJobNo.Rows.Count - 1; i  )
        {
            if (Boolean.Parse(dgvJobNo.Rows[i].Cells[2].Value.ToString()))
            {
var row= new Rows();
var a = new DefaultCellStyle();
a.BackColor= Color.Blue;
row.DefaultCellStyle = a;
                dgvJobNo.Rows[i] = row;
            }
        }
  • Related