我实际上已经解决了这个问题,但我是为后人发布的。

我的双显示器系统上的DataGridView遇到了一个非常奇怪的问题。该问题表现为对控件的极快重复(完全重绘的30秒),但只有当它在我的一个屏幕上时才会显示。在另一方面,重绘速度很好。

我有一个Nvidia 8800 GT,带有最新的非beta驱动程序(175。某事)。这是驱动程序错误吗?我会把它留在空中,因为我必须忍受这种特殊的配置。 (虽然在ATI卡上不会发生......)

绘画速度与单元格内容无关,自定义绘图根本不会改善性能 - 即使只绘制实心矩形也是如此。

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

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

此方法还允许我在应用程序运行时应用修复,以便更容易看到WPF库在我的表单上发生了哪些变化(使用Spy ++)。

经过大量的试验和错误后,我注意到在控件本身启用双缓冲(而不仅仅是表单)可以解决问题!


因此,您只需要根据DataGridView创建一个自定义类,以便启用其DoubleBuffering。就是这样!

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

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

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

有帮助吗?

解决方案

您只需要根据DataGridView创建一个自定义类,以便启用其DoubleBuffering。就是这样!


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

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

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

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

其他提示

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

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

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

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

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

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

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

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

在进行大数据更新后调用DrawingControl.ResumeDrawing(控件)。

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

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

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

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

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

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

希望这个技巧也适合你。

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

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

在任何ATI卡上都没有出现此问题(发生了开发)。

最佳:

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

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

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top