将今天的日期与一列进行比较并突出显示该单元格

Compare today's date to a column and highlight the cell

在我的 winform 应用程序中,有一个名为“Next_Calibration_On”的列(格式为“dd-MM-yyyy”),我必须与它比较今天的日期,如果它小于它,那么我想要以红色突出显示数据网格视图中的单元格。 我有以下代码:

        private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
    {
        DateTime currentToday = (DateTime)this.dataGridView1.Rows[e.RowIndex].Cells["Next_Calibration_On"].Value;

        if (currentToday <= DateTime.Now.Date)
        {
            e.CellStyle.ForeColor = Color.Red; //Font Color
            e.CellStyle.SelectionForeColor = Color.Red; //Selection Font color
        }

然而,这显示错误消息如下:

System.ArgumentException: 'Column named Next_Calibration_On cannot be found.Parameter name: columnName'

但是我的 table 中确实有专栏..如何解决这个问题?

不要编辑您的单元格。告诉您的 DataGridView 从何处获取其数据

不要将数据直接放在DataGridView 中,告诉它从哪里获取它的数据,以及如何显示获取的数据。这将您的数据与其显示方式分开。每当您决定以不同方式显示它时,您不必更改原始数据。

您可以看到数据和显示之间的这种分离是如何在电子表格中完成的。电子表格数据可以显示为按列和行排列的 collection 单元格,并显示单元格中值的文本表示。

显示相同数据的另一种方法是在图表中显示:即使显示与 collection 单元格完全不同,但背后的数据是相同的。

同样的数据,不同的显示。

在 winforms 中,所有显示数据序列的控件都有相同的分隔:ListBox、ListView、DataGridView、Chart 等。

所有这些 classes 都提供了直接将数据添加到组件的可能性,但它们也有一个 属性 DataSource.

如果您将序列分配给数据源,例如列表或数组,则会显示数据。其他属性决定数据源的每个元素必须如何显示。

在 DataGridView 中,DataGridViewColumns 定义必须显示每行的 属性 以及它们必须如何显示。

最简单的形式:

假设您有一个 class 客户:

class Customer
{
    public int Id {get; set;}
    public string Name {get; set;}
    public Address Address {get; set;}
    public string PhoneNr {get; set;}
};

假设您还有一个仅显示客户 ID 和姓名的 DataGridView

DataGridViewColumn columnCustomerId = new DateGridViewColumn
{
    DataPropertyName = nameof(Customer.Id),
    ValueType = typeof(int),
};
DataGridViewColumn columnCustomerName = new DateGridViewColumn
{
    DataPropertyName = nameof(Customer.Name),
    ValueType = typeof(string),
};
DataGridView customersDataGridView = new DataGridView(...) {...}
customersDataGridView.Columns.Add(columnCustomerId);
customersDataGridView.Columns.Add(columnCustomerName);

现在向客户展示:

List<Customer> customers = FetchCustomers(...);
customersDataGridview.DataSource = customers;

这足以显示客户的 ID 和姓名。如果您还想显示电话号码,只需添加一个列。您不必更改原始数据。

同样,如果您想要以不同方式格式化某些客户的单元格:这对您的原始客户没有影响 collection。只有显示发生变化。

旁注

虽然上述方法有效,但如果操作员编辑显示的数据,您仍然会遇到问题。如果要自动更新操作员所做的更改,请不要将数据放在列表中,而是放在 System.ComponentModel.BindingList:

BindingList<Customer> customers = new BindingList<Customer>(FetchCustomers(...));
customerDataGridView.DataSource = customers;

现在,如果操作员从 DataGridView 添加/删除/更改 Customers,它们会在 BindingList 中自动更新。

还有一个方便的提示:如果您希望通过单击列 header 进行自动排序,并且如果您希望轻松过滤:“仅显示居住在 'Amsterdam' 的客户”而无需更改Customers 列表,考虑使用 Nuget package BindingListView

BindingListView<Customer> customers = new BindingListView<Customer>(FetchCustomers(...));
customerDataGridView.DataSource = customers;

转眼之间:你有可排序的列;可编辑的客户。筛选很简单:

// show only Amsterdam customers:
customers.ApplyFilter( customer => customer.Address.City == "Amsterdam");

即使您没有在 DataGridView 中显示客户的城市,您也可以轻松地对其进行过滤。

顺便说一下:你有没有注意到我从 DataSource 切换是多么容易,甚至没有更改我的 DataGridView?这是因为我把显示和源数据分开了。

回到你的问题

您想更改 NextCalibration 列中单元格的布局。值高于 xxx 的每个单元格都应为红色。

单元格的布局在 DataGridViewCellStyle 中完成。如果您的 Cell 没有自己的 CellStyle,它会使用 DataGridViewColumn 的 CellStyle。

您想在显示的值 属性 满足某些要求时更改单元格样式。在这种情况下:如果小于今天的日期,前景色为红色,否则为默认值。复制列的默认单元格样式。

DataGridViewColumn columnNextCalibration = ...
DataGridViewCellStyle specialCellStyle = (DataGridViewCellStyle)columnNextCalibration
    .CellTemplate
    .CellStyle
    .Clone();

如果您的列没有特殊样式(它为空),请使用 InheritedStyle。

// adjust the CellStyle to the style it should have when the value meets the requirements
specialCellStyle.ForeColor = Color.Red;
// if desired: change the Font, the fontSize, or the BackColor, the frame whatever!

您想 select 默认 CellStyle (null) 或此特殊单元格样式,具体取决于显示的 DateTime 值:

现在您有一个特殊的 CellStyle,等待显示的值发生变化:

this.customersDataGridView.CellValueChanges += OnCellValueChanged;

private void OnCellValueChanged(object sender, DataGridViewCellEventArgs eventArgs)
{
    DataGridView dgv = (DataGridView)sender;

    // is the changed cell from my column in my DataGridView?
    if (Object.ReferencEquals(sender, customersDataGridView)
    && e.ColumnIndex == columnNextCalibation.DisplayIndex)
    {
        DataGridViewCell changedCell = dgv.Rows[e.RowIndex].Column[e.ColumnIndex];
        DateTime cellValue = (DateTime)cell.Value;
        DateTime today = ...
        if (cellValue < today)
        {
            // use the special CellStyle:
            changedCell.CellStyle = specialCellStyle;
        }
        else
        {
            // use the default CellStyle of the column, not a special one for this cell
            changedCell.CellStyle = null;
        }
    }
}