在我的两个屏幕之一的DataGridView可怕的重绘性能

我已经解决了这个问题,但是我把它贴出来留给后人。

我遇到了双监视器系统上DataGridView的一个非常奇怪的问题。 这个问题performance为一个极其缓慢的控制重绘( 像完整重绘30秒 ),但只有当它在我的屏幕之一。 另一方面,重绘速度很好。 我有一个nVidia 8800 GT与最新的非testing版驱动程序(175.东西)。 这是一个驱动程序错误? 我会把它留在空中,因为我必须忍受这个特殊的configuration。 (尽pipe…不会发生在ATI卡上)

油漆速度与单元格内容无关,而且自定义绘图根本无法改善性能 – 即使只是绘制一个坚实的矩形。

后来我发现在表单上放置一个ElementHost(来自System.Windows.Forms.Integration命名空间)可以解决问题。 它不必被搞砸,它只需要是DataGridView所在窗体的子项。 只要Visible属性为true,就可以将其调整为(0,0)。

不想明确地将.Net 3 / 3.5依赖项添加到我的应用程序中,我创build了一个方法来在运行时(如果可以的话)使用reflection来创build此控件。 它起作用了,至less它在没有所需库的机器上优雅地失败 – 它只是变得很慢。

这个方法也可以让我在应用程序运行的时候进行修复,使得更容易看到WPF库在我的表单上改变了什么(使用Spy ++)。

经过大量的反复试验之后,我注意到在控件本身(而不是表单)上启用双缓冲可以纠正问题!


所以,您只需要基于DataGridView创build一个自定义类,以便启用它的DoubleBuffering。 而已!

class CustomDataGridView: DataGridView { public CustomDataGridView() { DoubleBuffered = true; } } 

只要我所有的网格实例都使用这个定制版本,一切都很好。 如果我遇到由于无法使用子类解决scheme(如果我没有代码)而导致的情况,我想我可以尝试将该控件注入到表单中:) 会更有可能尝试使用reflection从外部强制DoubleBuffered属性,以再次避免依赖 )。

很遗憾,这么一个简单的东西吃了这么多的时间…

您只需要基于DataGridView创build自定义类,以便启用它的DoubleBuffering。 而已!

 class CustomDataGridView: DataGridView { public CustomDataGridView() { DoubleBuffered = true; } } 

只要我所有的网格实例都使用这个定制版本,一切都很好。 如果我遇到由于无法使用子类解决scheme(如果我没有代码)而导致的情况,我想我可以尝试将该控件注入到表单中:)会更有可能尝试使用reflection从外部强制DoubleBuffered属性,以再次避免依赖)。

很遗憾,这么一个简单的东西吃了这么多的时间…

注意:答案是答案,所以问题可以被标记为已回答

这是一些代码,使用reflection来设置属性,而不是像Benoit所说的那样进行子类化。

 typeof(DataGridView).InvokeMember( "DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, myDataGridViewObject, new object[] { true }); 

对于在vb.net中search的人来说,这里是代码

 DataGridView1.GetType.InvokeMember("DoubleBuffered", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.SetProperty, Nothing, DataGridView1, New Object() {True}) 

感谢Brian的原始代码

这个答案也适用于我。 我想我会添加一个改进,我认为应该是实施解决scheme的任何人的标准做法。

除非在远程桌面下将用户界面作为客户端会话运行,特别是在可用networking带宽较低的情况下,该解决scheme运行良好。 在这种情况下,通过使用双缓冲可以使性能变差。 因此,我build议以下为更完整的答案:

 class CustomDataGridView: DataGridView { public CustomDataGridView() { // if not remote desktop session then enable double-buffering optimization if (!System.Windows.Forms.SystemInformation.TerminalServerSession) DoubleBuffered = true; } } 

有关更多详细信息,请参阅检测远程桌面连接

添加到以前的post,对于Windows窗体应用程序,这是我用于DataGridView组件,使他们快速。 下面是类DrawingControl的代码。

 DrawingControl.SetDoubleBuffered(control) DrawingControl.SuspendDrawing(control) DrawingControl.ResumeDrawing(control) 

在构造函数中的InitializeComponent()之后调用DrawingControl.SetDoubleBuffered(control)。

在执行大数据更新之前调用DrawingControl.SuspendDrawing(control)。

大数据更新后调用DrawingControl.ResumeDrawing(控制)。

最后2个最好用try / finally块来完成。 (甚至可以更好地将类重写为IDisposable并在构造函数中调用SuspendDrawing() ,在Dispose()调用SuspendDrawing() Dispose()

 using System.Runtime.InteropServices; public static class DrawingControl { [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam); private const int WM_SETREDRAW = 11; /// <summary> /// Some controls, such as the DataGridView, do not allow setting the DoubleBuffered property. /// It is set as a protected property. This method is a work-around to allow setting it. /// Call this in the constructor just after InitializeComponent(). /// </summary> /// <param name="control">The Control on which to set DoubleBuffered to true.</param> public static void SetDoubleBuffered(Control control) { // if not remote desktop session then enable double-buffering optimization if (!System.Windows.Forms.SystemInformation.TerminalServerSession) { // set instance non-public property with name "DoubleBuffered" to true typeof(Control).InvokeMember("DoubleBuffered", System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic, null, control, new object[] { true }); } } /// <summary> /// Suspend drawing updates for the specified control. After the control has been updated /// call DrawingControl.ResumeDrawing(Control control). /// </summary> /// <param name="control">The control to suspend draw updates on.</param> public static void SuspendDrawing(Control control) { SendMessage(control.Handle, WM_SETREDRAW, false, 0); } /// <summary> /// Resume drawing updates for the specified control. /// </summary> /// <param name="control">The control to resume draw updates on.</param> public static void ResumeDrawing(Control control) { SendMessage(control.Handle, WM_SETREDRAW, true, 0); control.Refresh(); } } 

只是添加我们所做的来解决这个问题:升级到最新的NVIDIA驱动程序解决了这个问题。 没有代码必须重写。

为了完整起见,该卡是2008年3月发行的带有驱动程序的NVIDIA Quadro NVS 290(第169节)。 升级到最新版本(2009年2月的第182版)显着改善了我所有控件的绘制事件,特别是DataGridView。

这个问题在任何ATI卡上都没有看到(开发地点)。

我find了解决问题的办法。 转到高级显示属性中的疑难解答选项卡,并检查硬件加速滑块。 当我从IT公司获得新的公司电脑时,它已经被设置为一个完整的logging,并且我没有任何数据网格问题。 一旦我更新了video卡驱动程序并将其设置为已满,DataGrid控件的绘制变得非常缓慢。 所以我把它重置回原来的位置,问题就消失了。

希望这个技巧也适合你。

最好!!!!:Private Declare Function SendMessage Lib“user32”Alias“SendMessageA”(ByVal hWnd As Integer,ByVal wMsg As Integer,ByVal wParam As Integer,ByRef lParam As Object)As Integer Const WM_SETREDRAW As Integer =&HB

 Public Sub SuspendControl(this As Control) SendMessage(this.Handle, WM_SETREDRAW, 0, 0) End Sub Public Sub ResumeControl(this As Control) RedrawControl(this, True) End Sub Public Sub RedrawControl(this As Control, refresh As Boolean) SendMessage(this.Handle, WM_SETREDRAW, 1, 0) If (refresh) Then this.Refresh() End Sub 

在双监视器系统上,我们遇到了使用.NET 3.0和DataGridView的类似问题。

我们的应用程序将显示灰色背景的网格,表明单元格不能更改。 当select“更改设置”button时,程序会将单元格的背景颜色改变为白色,以向用户指示单元格文本可以被改变。 “取消”button会将上述单元格的背景颜色改回灰色。

随着背景颜色的改变,会出现一个闪烁,一个默认大小的网格的简单印象,具有相同数量的行和列。 这个问题只会发生在主监视器(从不是次要的)上,并且不会发生在单个监视器系统上。

双缓冲控制,使用上面的例子,解决了我们的问题。 我们非常感谢您的帮助。