使用 DataGridView 的人经常倾向于直接摆弄 Cells 和值的显示方式。使用 DataBinding 更容易:使用 DataGridView.DataSource 将数据与其显示方式分开。
使用数据源
我对你在 DataGridView 中显示的内容一无所知,所以假设你想显示 Products。
所以在某个地方你有一个类可以从某个地方保存和检索产品。当然,您隐藏了数据是从数据库中获取的,以及如何获取的(DbConnection 和 DbCommand?或者可能是实体框架和 LINQ?)。您所知道的是,您可以存储产品并在以后检索它们:
interface IRepository
{
int AddProduct(Product product) // returns Id
Product FetchProduct(int id);
IEnumerable<Product> FetchProducts(...); // fetches several Products to display
...
哦,在我忘记之前:
class Product
{
public int Id {get; set;}
public string Name {get; set;}
public decimal Price {get; set;}
public int Stock {get; set;}
...
}
假设您有一个带有 DataGridView 的表单,您想在其中显示一些产品。使用 Visual Studio 设计器,您添加了一个 DataGridView 和一些列
在构造函数中:
public MyForm : ...
{
InitializeComponent();
this.columnId.DataPropertyName = nameof(Product.Id);
this.columnName.DataPropertyName = nameof(Product.Name);
this.columnPrice.DataPropertyName = nameof(Product.Price);
...
}
当然,您可以访问存储库和获取要显示的产品的方法:
private IRepository ProductRepository => ...
private IEnumerable<Product> FetchProductsToDisplay()
{
return this.ProductRepository.FetchProducts(...);
}
现在要显示获取的产品,我们在 BindingList 中使用 DataGridView 的 DataSource:
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());
当然,在加载表单时:
private void OnFormLoading(object sender, ...)
{
this.ShowInitialProducts();
}
这足以显示所有产品。如果操作员添加或删除某些行,或编辑行的值,它们会自动更新。如果操作员表明他已经完成了对产品的编辑,例如通过单击确定按钮:
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);
}
顺便说一句,您是否注意到我的程序总是很小:它们只做一件事,它们服务于一个目的。这使它们易于理解、易于重用、易于测试和更改。
间奏曲:一些有用的功能
以下小方法可能会很方便。如果您有 DataGridViewRow,则可以通过 DataGridViewRow.DataBoundItem 获取行中显示的 Product。
// 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>();
}
回到你的问题
显然,有一个谓词让你想给一些单元格一个不同的背景颜色。例如:如果库存为零,您想要红色,如果库存低,您想要橙色。否则你想要一个白色的背景
Color ZeroStockColor = Color.Red;
Color LowStockColor = Color.Orange;
int LowStockValue = 10; // 10 or less: Stock is low
就在显示单元格的值之前,您有机会使用事件DataGridView.CellFormatting 格式化单元格。
如果显示股票的列正在格式化,我们要检查股票的价值。如果它是零,我们想要红色背景,如果它是低的,我们有一个橙色背景。
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;
}
}
}
如果DataGridViewCell.Style 不为空,则此样式用于格式化单元格。如果 Style 为 null,则使用 DataGridViewColumn.DefaultCellStyle,除非该值也为 null。在这种情况下,我们会走得更高:DataGridView.DefaultCellStyle
结论
如果您使用DataBinding,可以很容易地在DataGridView中显示您的数据并访问DataGridView中的对象:当前对象、选定对象或第10行显示的对象。
要格式化单元格,请使用DataGridViewCell.Style,或列中或DataGridView 中的DefaultCellStyle
使用事件DataGridView.CellFormatting 在单元格显示之前对其进行格式化。