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


81

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

Tôi đã gặp phải một vấn đề rất kỳ lạ với DataGridView trên hệ thống màn hình kép của mình. Vấn đề tự biểu hiện dưới dạng sơn lại điều khiển CỰC KỲ chậm ( như 30 giây để sơn lại toàn bộ ), nhưng chỉ khi nó ở trên một trong các màn hình của tôi. Khi khác, tốc độ sơn lại là ổn.

Tôi có Nvidia 8800 GT với trình điều khiển không phải beta mới nhất (175. gì đó). Nó có phải là một lỗi trình điều khiển? Tôi sẽ để điều đó trong không khí, vì tôi phải sống với cấu hình cụ thể này. (Tuy nhiên, nó không xảy ra trên thẻ ATI ...)

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

Sau đó, 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) trên biểu mẫu sẽ khắc phục được sự cố. Nó không phải bị rối với; nó chỉ cần là con của dạng DataGridView cũng có trên. Nó có thể được thay đổi kích thước thành (0, 0) miễn là thuộc tính Visible là đúng.

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

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

Sau rất nhiều lần thử và sai, tôi nhận thấy rằng việc bật bộ đệm kép trên chính điều khiển (trái ngược với chỉ biểu mẫu) sẽ khắc phục được sự cố!


Vì vậy, bạn chỉ cần tạo một lớp tùy chỉnh dựa trên DataGridView để 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 phiên bản lưới của tôi đang sử dụng phiên bản tùy chỉnh này, tất cả đều ổn. Nếu tôi từng gặp phải trường hợp do điều này gây ra khi 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ử đưa đ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 phản xạ để buộc thuộc tính DoubleBuffered từ bên ngoài vào để một lần nữa tránh sự phụ thuộc ).

Thật đáng buồn khi một thứ đơn giản tầm thường như vậy đã ăn hết thời gian của tôi ...


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

Bất kỳ ai cũng biết và có thể giải thích tại sao điều này xảy ra và tại sao DoubleBuffered không thể được bật theo mặc định?
Vojtěch Dohnal

Câu trả lời:


64

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


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

Miễn là tất cả các phiên bản lưới của tôi đang sử dụng phiên bản tùy chỉnh này, tất cả đều ổn. Nếu tôi từng gặp phải tình huống do điều này gây ra khi 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ử đưa đ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 phản xạ để buộc thuộc tính DoubleBuffered từ bên ngoài vào để một lần nữa tránh sự phụ thuộc).

Thật đáng buồn khi một thứ đơn giản tầm thường như vậy đã ăn hết thời gian của tôi ...

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


1
Làm thế nào bạn có thể làm điều này với Windows Forms Integration cho WPF ??
Một phần

Cảm ơn vì câu trả lời. Làm thế nào bạn đôi khi không thể sử dụng giải pháp lớp con? (Tôi không hiểu bit "nếu tôi không có mã").
Dan W

Tuyệt diệu! Hoạt động như một sự quyến rũ trong dự án của tôi đang bị chậm lại kỳ lạ cả về việc điền và cuộn bảng (:
knut

61

Đây là một số mã thiết lập thuộc tính bằng cách sử dụng phản chiếu, không có phân lớp như Benoit đề xuất.

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

3
Rất vui được giúp đỡ! Tôi gần như không đăng nó vì câu hỏi này đã có một năm.
Brian Ensink

1
Không, nó sẽ luôn giúp ích cho ai đó trong tương lai, chẳng hạn như thậm chí có thể là tôi, người vừa tìm thấy chủ đề này từ Google. Cảm ơn! Btw, có nên đặt nó trong phần Form1_Load không?
Dan W

2
Chỉ để cung cấp cho người khác tìm thấy điều này một ý tưởng: Đây là một phương pháp mở rộng hữu ích trên Controllớp. public static void ToggleDoubleBuffered(this Control control, bool isDoubleBuffered).
Anthony

Nó có thể được đặt trên sự kiện tải của FOrm nơi đặt chế độ xem lưới dữ liệu không?
Arie

18

Đối với những người đang 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})

10

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. Dưới đây là mã cho lớp DrawingControl.

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

Gọi DrawingControl.SetDoubleBuffered (điều khiển) 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 cập nhật dữ liệu lớn.

2 cuối cùng này được thực hiện tốt nhất với một khối thử / cuối cùng. (hoặc tốt hơn nữa là viết lại lớp dưới dạng IDisposablevà 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

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ẽ bổ sung một sự cải tiến mà tôi nghĩ nên là thông lệ tiêu chuẩn cho bất kỳ ai thực hiện giải pháp.

Giải pháp hoạt động tốt ngoại trừ khi giao diện người dùng đang được chạy dưới dạng phiên khách trong máy tính từ xa, đặc biệt là khi băng thông mạng khả dụng thấp. Trong trường hợp như vậy, hiệu suất có thể trở nên tồi tệ hơn khi sử dụng bộ đệm kép. Do đó, tôi đề xuất những điều sau như một câu trả lời đầ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


1

Tôi đã tìm thấy một giải pháp cho vấn đề. Chuyển đến 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 máy tính công ty mới của mình từ CNTT, nó đã được đặt thành một tích tắc từ đầy đủ và tôi không gặp bất kỳ vấn đề nào với datagrids. Sau khi tôi cập nhật trình điều khiển card màn hình và đặt nó thành đầy đủ, việc sơn các điều khiển datagrid trở nên rất chậm. Vì vậy, tôi đã đặt lại nó về vị trí cũ và sự cố đã biến mất.

Hy vọng thủ thuật này cũng hiệu quả với bạn.


1

Chỉ để thêm những gì chúng tôi đã làm để khắc phục sự cố 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ã phải được viết lại.

Để hoàn thiện, thẻ là Nvidia Quadro NVS 290 với trình điều khiển ngày tháng 3 năm 2008 (v. 169). Nâng cấp lên phiên bản mới nhất (phiên bản 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à đối với DataGridView.

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


1

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

0

Chúng tôi đã gặp sự cố tương tự khi sử dụng .NET 3.0 và DataGridView trên hệ thống màn hình kép.

Ứng dụng của chúng tôi sẽ hiển thị lưới với nền màu xám, cho biết rằng không thể thay đổi các ô. Khi chọn nút "thay đổi cài đặt", chương trình sẽ thay đổi màu nền của ô thành màu trắng để cho người dùng biết rằng văn bản ô có thể được thay đổi. Nút "hủy" sẽ thay đổi màu nền của các ô nói trên trở lại màu xám.

Khi màu nền thay đổi, sẽ có hiện tượng nhấp nháy, một ấn tượng ngắn về lưới có kích thước mặc định với cùng số hàng và cột. Sự cố này chỉ xảy ra trên màn hình chính (không bao giờ là màn hình phụ) và nó sẽ không xảy ra trên một hệ thống màn hình duy nhất.

Bộ đệm kép điều khiển, sử dụng ví dụ trên, đã giải quyết được vấn đề của chúng tôi. Chúng tôi đánh giá rất cao sự giúp đỡ của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.