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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/118528/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 10:01:46  来源:igfitidea点击:

Horrible redraw performance of the DataGridView on one of my two screens

.netwinformsperformancedatagridviewnvidia

提问by Corey Ross

I've actually solved this, but I'm posting it for posterity.

我实际上已经解决了这个问题,但我将其发布给后代。

I ran into a very odd issue with the DataGridView on my dual-monitor system. The issue manifests itself as an EXTREMELY slow repaint of the control (like 30 seconds for a full repaint), but only when it is on one of my screens. When on the other, the repaint speed is fine.

我的双显示器系统上的 DataGridView 遇到了一个非常奇怪的问题。该问题表现为控件的重绘非常缓慢(例如完全重绘需要 30 秒),但仅当它出现在我的一个屏幕上时。另一方面,重绘速度很好。

I have an Nvidia 8800 GT with the latest non-beta drivers (175. something). Is it a driver bug? I'll leave that up in the air, since I have to live with this particular configuration. (It does not happen on ATI cards, though...)

我有一台 Nvidia 8800 GT 和最新的非测试版驱动程序(175。某事)。是驱动程序错误吗?我将把它悬而未决,因为我必须忍受这种特殊的配置。(不过,它不会发生在 ATI 卡上……)

The paint speed has nothing to do with the cell contents, and custom drawing doesn't improve the performance at all - even when just painting a solid rectangle.

绘制速度与单元格内容无关,自定义绘制根本不会提高性能 - 即使只是绘制实心矩形。

I later find out that placing a ElementHost (from the System.Windows.Forms.Integration namespace) on the form corrects the problem. It doesn't have to be messed with; it just needs to be a child of the form the DataGridView is also on. It can be resized to (0, 0) as long as the Visibleproperty is true.

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

I don't want to explicitly add the .NET 3/3.5 dependency to my application; I make a method to create this control at runtime (if it can) using reflection. It works, and at least it fails gracefully on machines that don't have the required library - it just goes back to being slow.

我不想将 .NET 3/3.5 依赖项显式添加到我的应用程序中;我使用反射创建了一种在运行时(如果可以)创建此控件的方法。它可以工作,至少它在没有所需库的机器上正常失败 - 它只是恢复缓慢。

This method also lets me apply to fix while the app is running, making it easier to see what the WPF libraries are changing on my form (using Spy++).

此方法还允许我在应用程序运行时申请修复,从而更容易查看 WPF 库在我的表单上正在更改的内容(使用 Spy++)。

After a lot of trial and error, I notice that enabling double buffering on the control itself (as opposed to just the form) corrects the issue!

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



So, you just need to make a custom class based off of DataGridView so you can enable its DoubleBuffering. That's it!

因此,您只需要基于 DataGridView 创建一个自定义类,以便启用它的 DoubleBuffering。就是这样!

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

As long as all of my instances of the grid are using this custom version, all is well. If I ever run into a situation caused by this where I'm not able to use the subclass solution (if I don't have the code), I suppose I could try to inject that control onto the form :) (although I'll be more likely to try using reflection to force the DoubleBuffered property on from the outside to once again avoid the dependency).

只要我所有的网格实例都使用这个自定义版本,一切都很好。如果我遇到因此无法使用子类解决方案(如果我没有代码)的情况,我想我可以尝试将该控件注入表单:)(尽管我将更有可能尝试使用反射从外部强制 DoubleBuffered 属性再次避免依赖)。

It is sad that such a trivially simple thing ate up so much of my time...

很遗憾,这么简单的事情占用了我这么多时间......

采纳答案by Benoit

You just need to make a custom class based off of DataGridView so you can enable its DoubleBuffering. That's it!

您只需要基于 DataGridView 创建一个自定义类,以便启用它的 DoubleBuffering。就是这样!


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

As long as all of my instances of the grid are using this custom version, all is well. If I ever run into a situation caused by this where I'm not able to use the subclass solution (if I don't have the code), I suppose I could try to inject that control onto the form :) (although I'll be more likely to try using reflection to force the DoubleBuffered property on from the outside to once again avoid the dependency).

只要我所有的网格实例都使用这个自定义版本,一切都很好。如果我遇到因此无法使用子类解决方案(如果我没有代码)的情况,我想我可以尝试将该控件注入表单:)(尽管我将更有可能尝试使用反射从外部强制 DoubleBuffered 属性再次避免依赖)。

It is sad that such a trivially simple thing ate up so much of my time...

很遗憾,这么简单的事情占用了我这么多时间......

Note: Making the answer an answer so the question can be marked as answered

注意:使答案成为答案,以便可以将问题标记为已回答

回答by Brian Ensink

Here is some code that sets the property using reflection, without subclassing as Benoit suggests.

下面是一些使用反射设置属性的代码,没有像 Benoit 建议的那样子类化。

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

回答by GELR

For people searching how to do it in VB.NET, here is the code:

对于在 VB.NET 中搜索如何做的人,这里是代码:

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

回答by brtmckn

Adding to previous posts, for Windows Forms applications this is what I use for DataGridView components to make them fast. The code for the class DrawingControl is below.

添加到以前的帖子中,对于 Windows 窗体应用程序,这是我用于 DataGridView 组件以使其快速的方法。类 DrawingControl 的代码如下。

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

Call DrawingControl.SetDoubleBuffered(control) after InitializeComponent() in the constructor.

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

Call DrawingControl.SuspendDrawing(control) before doing big data updates.

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

Call DrawingControl.ResumeDrawing(control) after doing big data updates.

做大数据更新后调用DrawingControl.ResumeDrawing(control)。

These last 2 are best done with a try/finally block. (or even better rewrite the class as IDisposableand call SuspendDrawing()in the constructor and ResumeDrawing()in Dispose().)

最后 2 个最好用 try/finally 块完成。(甚至更好地将类重写为 asIDisposableSuspendDrawing()在构造函数和ResumeDrawing()in 中调用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();
    }
}

回答by Kev

The answer to this worked for me too. I thought I would add a refinement that I think should be standard practise for anyone implementing the solution.

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

The solution works well except when the UI is being run as a client session under remote desktop, especially where the available network bandwidth is low. In such a case, performance can be made worse by the use of double-buffering. Therefore, I suggest the following as a more complete answer:

该解决方案运行良好,除非 UI 作为远程桌面下的客户端会话运行,尤其是在可用网络带宽较低的情况下。在这种情况下,使用双缓冲会使性能变差。因此,我建议将以下内容作为更完整的答案:

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

For more details, refer to Detecting remote desktop connection

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

回答by user3727004

Best!:

最好的事物!:

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 If
End Sub

回答by user3727004

I found a solution to the problem. Go to troubleshoot tab in the advanced display properties and check the hardware acceleration slider. When I got my new company PC from IT, it was set to one tick from full and I didn't have any problems with datagrids. Once I updated the video card driver and set it to full, painting of datagrid controls became very slow. So I reset it back to where it was and the problem went away.

我找到了解决问题的方法。转到高级显示属性中的疑难解答选项卡并检查硬件加速滑块。当我从 IT 部门拿到我的新公司 PC 时,它被设置为从满格开始一滴答,我对数据网格没有任何问题。一旦我更新了显卡驱动程序并将其设置为完整,数据网格控件的绘制变得非常缓慢。所以我将它重置回原来的位置,问题就消失了。

Hope this trick works for you as well.

希望这个技巧也适用于你。

回答by Richard Morgan

Just to add what we did to fix this issue: We upgraded to the latest Nvidia drivers solved the problem. No code had to be rewritten.

只是添加我们为解决此问题所做的工作:我们升级到最新的 Nvidia 驱动程序解决了该问题。无需重写任何代码。

For completeness, the card was an Nvidia Quadro NVS 290 with drivers dated March 2008 (v. 169). Upgrading to the latest (v. 182 dated Feb 2009) significantly improved the paint events for all my controls, especially for the DataGridView.

为完整起见,该卡是 Nvidia Quadro NVS 290,驱动程序日期为 2008 年 3 月(v. 169)。升级到最新版本(2009 年 2 月的第 182 版)显着改善了我所有控件的绘制事件,尤其是 DataGridView。

This issue was not seen on any ATI cards (where development occurs).

在任何 ATI 卡(发生开发的地方)上都未发现此问题。

回答by Richard Morgan

We've experienced a similar problem using .NET 3.0 and DataGridView on a dual monitor system.

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

Our application would display the grid with a gray background, indicating that the cells could not be changed. Upon selecting a "change settings" button, the program would change the background color of the cells white to indicate to the user that the cell text could be changed. A "cancel" button would change the background color of the aforementioned cells back to gray.

我们的应用程序将显示带有灰色背景的网格,表明无法更改单元格。选择“更改设置”按钮后,程序会将单元格的背景颜色更改为白色,以向用户指示可以更改单元格文本。“取消”按钮会将上述单元格的背景颜色更改回灰色。

As the background color changed there would be a flicker, a brief impression of a default sized grid with the same number of rows and columns. This problem would only occur on the primary monitor (never the secondary) and it would not occur on a single monitor system.

随着背景颜色的变化,会有一个闪烁,一个具有相同行数和列数的默认大小网格的简要印象。这个问题只会发生在主监视器上(永远不会发生在辅助监视器上),它不会发生在单个监视器系统上。

Double buffering the control, using the above example, solved our problem. We greatly appreciated your help.

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