Câu hỏi Hiệu năng vẽ lại khủng khiếp của DataGridView trên một trong hai màn hình của tôi


Tôi đã thực sự giải quyết điều này, nhưng tôi đăng nó cho hậu thế.

Tôi chạy vào một vấn đề rất kỳ quặc với DataGridView trên hệ thống giám sát kép của tôi. Vấn đề thể hiện chính nó như là một repaint chậm chạp của kiểm soát (như 30 giây cho một repaint đầy đủ), nhưng chỉ khi nó ở trên một trong các màn hình của tôi. Khi mặt khác, tốc độ sơn lại là tốt.

Tôi có một Nvidia 8800 GT với các trình điều khiển non-beta mới nhất (175. một cái gì đó). Đó có phải là lỗi trình điều khiển không? Tôi sẽ để nó lên trên không, vì tôi phải sống với cấu hình đặc biệt này. (Nó không xảy ra trên thẻ ATI, mặc dù ...)

Tốc độ vẽ không liên quan gì đến nội dung của ô và bản vẽ tùy chỉnh không cải thiện hiệu suất - ngay cả khi chỉ vẽ một hình chữ nhật vững chắc.

Sau này tôi phát hiện ra rằng việc đặt một ElementHost (từ không gian tên System.Windows.Forms.Integration) vào biểu mẫu sẽ sửa lỗi. Nó không phải bị rối tung; nó chỉ cần là một đứa trẻ của biểu mẫu DataGridView cũng được bật. Nó có thể được thay đổi kích cỡ thành (0, 0) miễn là Có thể nhìn thấy tài sản là đúng sự thật.

Tôi không muốn thêm rõ ràng phụ thuộc .NET 3 / 3.5 vào ứng dụng của mình; Tôi tạo một phương thức để tạo điều khiển này khi chạy (nếu nó có thể) bằng cách sử dụng sự phản chiếu. Nó hoạt động, và ít nhất nó thất bại một cách duyên dáng trên các máy không có thư viện cần thiết - nó chỉ trở lại chậm.

Phương pháp này cũng cho phép tôi áp dụng để sửa lỗi trong khi ứng dụng đang chạy, giúp dễ dàng hơn để xem thư viện WPF đang thay đổi gì trên biểu mẫu của tôi (sử dụng Spy ++).

Sau nhiều thử nghiệm và lỗi, tôi nhận thấy rằng cho phép đệm đôi trên bản thân điều khiển (trái ngược với biểu mẫu) sửa lỗi này!


Vì vậy, bạn chỉ cần tạo một lớp tùy chỉnh dựa trên DataGridView để bạn có thể kích hoạt DoubleBuffering của nó. Đó là nó!

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

Miễn là tất cả các trường hợp của tôi của lưới điện đang sử dụng phiên bản tùy chỉnh này, tất cả là tốt. Nếu tôi từng gặp một tình huống gây ra bởi điều này, nơi tôi không thể sử dụng giải pháp lớp con (nếu tôi không có mã), tôi cho rằng tôi có thể thử tiêm điều khiển đó vào biểu mẫu :) (mặc dù tôi sẽ có nhiều khả năng thử sử dụng sự phản chiếu để buộc tài sản DoubleBuffered từ bên ngoài một lần nữa tránh sự phụ thuộc).

Thật buồn khi một thứ đơn giản tầm thường đã ăn quá nhiều thời gian của tôi ...


76
2017-09-23 01:01


gốc


Chúng tôi đã gặp sự cố tương tự với khách hàng có Multimon Cài đặt. Vì lý do gì, khi họ tắt Multimon, vấn đề sẽ biến mất. - BlueRaja - Danny Pflughoeft


Các câu trả lời:


Bạn chỉ cần tạo một lớp tùy chỉnh dựa trên DataGridView để bạn có thể kích hoạt DoubleBuffering của nó. Đó là nó!


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

Miễn là tất cả các trường hợp của tôi của lưới điện đang sử dụng phiên bản tùy chỉnh này, tất cả là tốt. Nếu tôi từng gặp một tình huống gây ra bởi điều này, nơi tôi không thể sử dụng giải pháp phân lớp (nếu tôi không có mã), tôi cho rằng tôi có thể thử tiêm điều khiển đó vào biểu mẫu :) (mặc dù tôi ' sẽ có nhiều khả năng thử sử dụng sự phản chiếu để buộc thuộc tính DoubleBuffered ở bên ngoài một lần nữa tránh sự phụ thuộc).

Thật buồn khi một thứ đơn giản tầm thường đã ăn quá nhiều thời gian của tôi ...

Lưu ý: Trả lời câu trả lời để câu hỏi có thể được đánh dấu là đã trả lời


60
2017-10-01 19:49



Làm thế nào bạn có thể làm điều này với Windows Forms Integration cho WPF ?? - Partial
Cảm ơn câu trả lời. Đôi khi bạn sẽ không thể sử dụng giải pháp phân lớp? (Tôi không hiểu "nếu tôi không có mã" bit). - Dan W
Tuyệt diệu! Làm việc như một sự quyến rũ trong dự án của tôi đã bị chậm lại kỳ lạ cả trên populating và di chuyển bảng (: - knut


Đây là một số mã đặt thuộc tính bằng cách sử dụng sự phản chiếu, không có phân lớp như Benoit gợi ý.

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

58
2018-04-24 18:52



Vui mừng được giúp đỡ! Tôi gần như không đăng nó bởi vì câu hỏi này đã được một tuổi. - Brian Ensink
Neah, nó sẽ luôn giúp đỡ ai đó trong tương lai, chẳng hạn như có thể ngay cả tôi, người vừa tìm thấy chuỗi này từ Google. Cảm ơn! Btw, là nó thích hợp hơn để đặt điều này trong phần Form1_Load? - Dan W
Chỉ cần cung cấp cho người khác tìm thấy ý tưởng này: Đây là phương pháp tiện ích hữu ích trên Control lớp học. public static void ToggleDoubleBuffered(this Control control, bool isDoubleBuffered). - Anthony


Đối với những người tìm kiếm cách thực hiện nó trong VB.NET, đây là mã:

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

13
2018-05-18 15:15





Thêm vào các bài viết trước, đối với các ứng dụng Windows Forms, đây là những gì tôi sử dụng cho các thành phần DataGridView để làm cho chúng nhanh chóng. Mã cho lớp DrawingControl ở dưới.

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

Gọi DrawingControl.SetDoubleBuffered (control) sau InitializeComponent () trong hàm tạo.

Gọi DrawingControl.SuspendDrawing (điều khiển) trước khi thực hiện cập nhật dữ liệu lớn.

Gọi DrawingControl.ResumeDrawing (điều khiển) sau khi thực hiện cập nhật dữ liệu lớn.

Hai cuối cùng này được thực hiện tốt nhất với khối thử / cuối cùng. (hoặc thậm chí tốt hơn viết lại lớp như IDisposable và gọi SuspendDrawing() trong hàm tạo và ResumeDrawing() trong 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();
    }
}

7
2018-04-13 16:45





Câu trả lời cho điều này cũng làm việc cho tôi. Tôi nghĩ rằng tôi sẽ thêm một tinh tế mà tôi nghĩ rằng nên được thực hành tiêu chuẩn cho bất cứ ai thực hiện các giải pháp.

Giải pháp này hoạt động tốt trừ khi giao diện người dùng đang được chạy dưới dạng phiên máy khách trong máy tính từ xa, đặc biệt khi băng thông mạng có sẵn thấp. Trong trường hợp này, hiệu suất có thể được thực hiện tồi tệ hơn bằng cách sử dụng bộ đệm đôi. Do đó, tôi đề xuất câu trả lời sau đầy đủ hơn:

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

Để biết thêm chi tiết, hãy tham khảo Phát hiện kết nối máy tính từ xa


6
2017-10-07 20:28





Tôi tìm thấy một giải pháp cho vấn đề. Đi tới tab khắc phục sự cố trong thuộc tính hiển thị nâng cao và kiểm tra thanh trượt tăng tốc phần cứng. Khi tôi nhận được công ty máy tính mới của tôi từ CNTT, nó đã được thiết lập để đánh dấu một từ đầy đủ và tôi không có bất kỳ vấn đề với datagrids. Khi tôi cập nhật trình điều khiển cạc video và đặt nó thành đầy đủ, việc vẽ các điều khiển dữ liệu trở nên rất chậm. Vì vậy, tôi đặt lại nó trở lại nơi nó đã và vấn đề đã biến mất.

Hy vọng mẹo này cũng phù hợp với bạn.


1
2018-02-26 22:35





Chỉ cần thêm những gì chúng tôi đã làm để khắc phục vấn đề này: Chúng tôi đã nâng cấp lên trình điều khiển Nvidia mới nhất đã giải quyết được sự cố. Không có mã nào được viết lại.

Để hoàn thành, thẻ là một Nvidia Quadro NVS 290 với trình điều khiển ngày tháng 3 năm 2008 (câu 169). Nâng cấp lên phiên bản mới nhất (v. 182 ngày tháng 2 năm 2009) cải thiện đáng kể các sự kiện sơn cho tất cả các điều khiển của tôi, đặc biệt là cho DataGridView.

Vấn đề này đã không được nhìn thấy trên bất kỳ thẻ ATI (nơi phát triển xảy ra).


1
2018-06-10 16:54





Tốt!:

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

1
2017-10-07 19:45