一、 问题描述
如果把DataGridView拖拽出显示屏的可视区域,然后再拖动滚动条显示DataGridView,我们发现表格里的数据刷新有问题而不能正常显示。
二、 问题重现
1. 在Vista或者Win7中,把主题设为Aero;
2. 在Visual Studio中创建一个Winforms工程;
3. 在Form1上添加一个DataGridView,并在属性(Properties)窗口为它添加三列;
4. 为Form1添加Load的事件相应器(Event Handler),并添加如下代码:
private void Form1_Load(object sender, EventArgs e)
{
for (int j = 0; j < 20; ++j)
{
DataGridViewRow row = new DataGridViewRow();
for (int i = 0; i < dataGridView1.Columns.Count; ++i)
{
DataGridViewTextBoxCell cell = new DataGridViewTextBoxCell();
cell.Value = "Row: " + j.ToString() + " Column: " + i.ToString();
row.Cells.Add(cell);
}
dataGridView1.Rows.Add(row);
}
}
5. 编译运行;
6. 把DataGridView中的横向滚动条拖到最右边;
7. 把Form1拖到屏幕的左边,让部分DataGridView不能显示;
8. 向左拖动DataGridView中的横向滚动条;
此时,DataGridView出现问题,表格里面的数据不能正常显示,如下图所示:
三、 问题分析
当我们向左拖动DataGridView的滚动条,让DataGridView左边之前在显示器可显示区域以外的数据时重新回到可显示的区域时,我们没有及时刷新这些区域。因此会出现显示重叠的问题。
四、 解决方案
Winforms将在.NET 4.0 SP1中解决掉这个问题。当.NET 4.0 SP1正式发布时,用户可以下载最新版本的.NET。
另外,由于问题出现的根源在于DataGridView在拖动滚动条没有及时刷新。因此只要我们只要在拖动滚动条时强制刷新以便能重新绘制DataGridView中的数据。此时该问题将迎刃而解。
基于这个思路,我们可以为例子中的dataGridView1添加一个Scroll事件相应器,并添加如下代码:
private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
{
dataGridView1.Invalidate();
}
上面这种方法固然简单。但当dataGridView1中的数据很多时,刷新整个区域将耗时很久,影响程序在拖动滚动条时的用户体验。我们需要一个性能更好的方案。
由于我们拖动滚动条时,只有一个很小的矩形区域是新出现在可显示区域的。如果我们仅仅只刷新这个很小的区域,性能将能得到极大的提升。于是,我们可以把代码改成:
private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
{
Rectangle rect;
DataGridView ctrl = (DataGridView)sender;
Point pt = PointToScreen(ctrl.Location);
if (pt.X < 0)
{
int left = -pt.X;
int top = ctrl.ColumnHeadersHeight;
int width = e.OldValue - e.NewValue;
int height = ctrl.ClientSize.Height;
ctrl.Invalidate(new Rectangle(new Point(left, top), new Size(width, height)));
}
pt.X += ctrl.Width;
rect = Screen.GetBounds(pt);
if (pt.X > rect.Right)
{
int left = ctrl.ClientSize.Width - (pt.X - rect.Right) - (e.NewValue - e.OldValue);
int top = ctrl.ColumnHeadersHeight;
int width = e.NewValue - e.OldValue;
int height = ctrl.ClientSize.Height;
ctrl.Invalidate(new Rectangle(new Point(left, top), new Size(width, height)));
}
pt.Y += ctrl.Height;
if (pt.Y > rect.Bottom)
{
int left = 0;
int top = ctrl.ColumnHeadersHeight;
int width = ctrl.ClientSize.Width;
int height = ctrl.ClientSize.Height - (pt.Y - rect.Bottom) - (e.NewValue - e.OldValue);
ctrl.Invalidate(new Rectangle(new Point(left, top), new Size(width, height)));
}
}