代码之家  ›  专栏  ›  技术社区  ›  Corey Ross

在我的两个屏幕中的一个屏幕上,DataGridView的重画性能非常糟糕

  •  80
  • Corey Ross  · 技术社区  · 17 年前

    我已经解决了这个问题,但我要把它传给后代。

    我在双监视器系统上遇到了DataGridView的一个非常奇怪的问题。这个问题表现为控件的重新绘制非常缓慢( 大概30秒就可以完全重新上漆了 ),但仅当它出现在我的一个屏幕上时。另一方面,重新绘制的速度很好。

    绘制速度与单元格内容无关,自定义绘图根本无法提高性能,即使只是绘制实心矩形也是如此。

    我后来发现,在表单上放置ElementHost(来自System.Windows.Forms.Integration命名空间)可以纠正此问题。它不必被弄乱;它只需要是DataGridView所在表单的子级。只要 看得见的

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

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

    经过多次尝试和错误,我注意到在控件本身(而不仅仅是窗体)上启用双缓冲可以纠正这个问题!


    所以,您只需要基于DataGridView创建一个自定义类,以便启用其双缓冲。就这样!

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

    只要我的所有网格实例都使用这个自定义版本,一切都很好。如果我遇到这样的情况,即我无法使用子类解决方案(如果我没有代码),我想我可以尝试将该控件注入表单:)( ).

    很遗憾,这么一件小事占用了我这么多时间。。。

    9 回复  |  直到 8 年前
        1
  •  66
  •   Benoit    17 年前

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

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

    很遗憾,这么一件小事占用了我这么多时间。。。

        2
  •  61
  •   Scott Stanchfield    16 年前

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

    typeof(DataGridView).InvokeMember(
       "DoubleBuffered", 
       BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
       null, 
       myDataGridViewObject, 
       new object[] { true });
    
        3
  •  19
  •   Peter Mortensen Pieter Jan Bonestroo    8 年前

    对于在VB.NET中搜索如何执行此操作的用户,以下是代码:

    DataGridView1.GetType.InvokeMember("DoubleBuffered", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.SetProperty, Nothing, DataGridView1, New Object() {True})
    
        4
  •  11
  •   Patrick from NDepend team    8 年前

    在前面的文章中,对于Windows窗体应用程序,这就是我用于DataGridView组件以使其快速运行的原因。下面是DrawingControl类的代码。

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

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

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

    最后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();
        }
    }
    
        5
  •  7
  •   Community Mohan Dere    8 年前

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

    有关详细信息,请参阅 Detecting remote desktop connection

        6
  •  1
  •   user185931 user185931    16 年前

    我找到了解决这个问题的办法。转到“高级显示属性”中的“疑难解答”选项卡,并检查硬件加速滑块。当我从它那里得到我的新公司PC时,它被设置为从满到满的一个刻度,并且我对数据网格没有任何问题。一旦我更新了显卡驱动程序并将其设置为full,datagrid控件的绘制就变得非常缓慢。所以我把它重置到原来的位置,问题就消失了。

    希望这个技巧对你也有用。

        7
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    8 年前

    为完整起见,该卡为Nvidia Quadro NVS 290,驾驶员日期为2008年3月(第169节)。升级到最新版本(日期为2009年2月的第182版)显著改进了所有my控件的绘制事件,尤其是DataGridView。

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

        8
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    8 年前

    最好的!:

    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
    
        9
  •  0
  •   James James    17 年前

    在双监视器系统上使用.NET3.0和DataGridView时,我们也遇到过类似的问题。

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