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;
}
}