65.9K
CodeProject 正在变化。 阅读更多。
Home

DataGridView 的单元格闪烁

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.63/5 (13投票s)

2007 年 9 月 7 日

Zlib

3分钟阅读

viewsIcon

145653

downloadIcon

3210

一篇关于为 DataGridView 添加单元格闪烁功能的文章

Screenshot - DataGridViewCellBlink.jpg

引言

在阅读了 Code Project 上的许多文章后,我意识到是时候做出贡献了。几个月前,我遇到了一个需要在 DataGridView 控件中的单元格在单元格值更改时闪烁的需求。此处提供的代码可以应用于任何其他网格。

网格单元格的闪烁以如下方式实现。当我们更新单元格的值时,我们也会将该单元格的背景颜色更改为闪烁颜色。为了将单元格背景颜色恢复为其原始值,我们运行一个后台线程,该线程遍历一个正在闪烁的单元格列表,并将它们重置为其原始的非闪烁状态。

代码

示例项目有两个函数。第一个函数 DataInputThreadFunc() 用于生成要在网格中填充/更新的随机值。第二个函数 GridBlinkThreadFunc() 用于将单元格恢复为非闪烁状态。

让我们看看第一个函数 DataInputThreadFunc()

private void DataInputThreadFunc()
{
    Random rand = new Random();
    while (true)
    {
        if (dataGridView1.IsDisposed)
            break;

        CellData data = new CellData();
        data.Row = rand.Next(0, 7);
        data.Col = rand.Next(0, 3);
        data.Time = DateTime.Now;

        int value = rand.Next(0, 101);

        dataGridView1.Invoke((MethodInvoker)delegate()
        {
            dataGridView1.Rows[data.Row].Cells[data.Col].Value = value;
            dataGridView1.Rows[data.Row].Cells[data.Col].Style
              .BackColor = Color.Salmon;
        });

        lock (_blinkData)
        {
            _blinkData.Add(data);
        }

        Thread.Sleep(1000);
    }
}

该函数使用 while (true) 循环,因为它是一个后台线程,并且会在应用程序关闭时自动关闭。 完成 if (dataGridView1.IsDisposed) 检查是为了确保我们不会在已释放的对象上调用 dataGridView1.Invoke()。当用户关闭应用程序时,可能会发生这种情况。

接下来,我们初始化 CellData 类的对象以存储闪烁数据

class CellData
{
     public int Row;
     public int Col;
     public DateTime Time;
}

此类用于存储行号、列号以及值更改的时间。

接下来,我们使用 dataGridView1.Invoke() 调用用户界面线程并设置网格属性。我们将闪烁数据保存在一个泛型列表中,以供闪烁线程函数稍后使用。由于该列表被多个线程更改,因此我们通过在每次访问时锁定列表来同步访问。

现在让我们看看闪烁线程函数

private void GridBlinkThreadFunc()
{
    while (true)
    {
        // Make a copy to avoid invalid operation exception
        // while iterating through the map
        List<CellData> tempBlinkData;
        lock (_blinkData)
        {
            tempBlinkData = new List<CellData>(_blinkData);
        }

        foreach (CellData data in tempBlinkData)
        {
            TimeSpan elapsed = DateTime.Now - data.Time;
            if (elapsed.TotalMilliseconds > 500) // 500 is the Blink delay
            {
                if (dataGridView1.IsDisposed)
                    return;

                dataGridView1.BeginInvoke((MethodInvoker)delegate()
                {
                    dataGridView1.Rows[data.Row].Cells[data.Col]
                      .Style.BackColor = dataGridView1.Columns[data.Col]
                      .DefaultCellStyle.BackColor;
                });

                lock (_blinkData)
                {
                    _blinkData.Remove(data);
                }
            }
        }

        Thread.Sleep(250); // Blink frequency
    }
}

首先,我们创建 _blinkData 列表的副本。这有助于我们在遍历临时副本的内容时修改列表。对于我们在列表中找到的每个单元格,我们检查闪烁时间是否已过。在这种情况下,闪烁时间是 500 毫秒。任何已过闪烁时间的单元格都会将其背景颜色重置为默认单元格样式背景颜色,并从列表中删除。

同样,我们确保仅在用户界面线程中设置网格属性。此外,我们在更改 _blinkData 列表之前将其锁定。 Thread.Sleep(250) 是我们遍历列表以关闭单元格的频率。理想情况下,它应该是闪烁延迟值的一半。

关注点

您会注意到此代码可以应用于任何网格。此代码也可以隐藏在扩展 DataGridView 控件的类中。

我喜欢 .NET 2.0 的一点是 dataGridView1.Invoke((MethodInvoker)delegate()。此语句让您免于编写函数和声明 delegate

"Kristof Verbiest" 提出了一个很好的观点,即使用 BeginInvoke() 代替 Invoke()GridBlinkThreadFunc() 使用 BeginInvoke() 来避免不必要的上下文切换。

历史

  • 2007 年 6 月 9 日:首次发布
  • 2007 年 11 月 9 日:更改 GridBlinkThreadFunc() 以使用 BeginInvoke() 代替 Invoke()
  • 2008 年 2 月 5 日:编辑了“要点”部分
© . All rights reserved.