DataGridView'ün iki ekranımdan birinde korkunç yeniden çizim performansı


81

Aslında bunu çözdüm ama gelecek nesillere gönderiyorum.

Çift monitör sistemimde DataGridView ile ilgili çok tuhaf bir sorunla karşılaştım. Sorun, kontrolün SON DERECE yavaş yeniden boyanması olarak kendini gösterir ( tam bir yeniden boyama için 30 saniye gibi ), ancak yalnızca ekranlarımdan birinde olduğunda. Diğer yandan, yeniden boyama hızı iyidir.

En son beta olmayan sürücülere sahip bir Nvidia 8800 GT'ye sahibim (175. bir şey). Bu bir sürücü hatası mı? Bu özel konfigürasyonla yaşamak zorunda olduğum için bunu havada bırakacağım. (Yine de ATI kartlarında olmuyor ...)

Boyama hızının hücre içeriğiyle hiçbir ilgisi yoktur ve özel çizim, yalnızca düz bir dikdörtgen boyarken bile performansı hiç iyileştirmez.

Daha sonra forma bir ElementHost yerleştirmenin (System.Windows.Forms.Integration ad alanından) sorunu düzelttiğini öğrendim. Onunla uğraşmak zorunda değilsiniz; DataGridView'ün de açık olduğu formun alt öğesi olması gerekir. Visible özelliği true olduğu sürece (0, 0) olarak yeniden boyutlandırılabilir .

.NET 3 / 3.5 bağımlılığını uygulamama açıkça eklemek istemiyorum; Bu denetimi çalışma zamanında (yapabiliyorsa) yansıma kullanarak oluşturmak için bir yöntem yapıyorum. Çalışır ve en azından gerekli kitaplığa sahip olmayan makinelerde sorunsuz bir şekilde başarısız olur - sadece yavaş olmaya geri döner.

Bu yöntem aynı zamanda uygulama çalışırken düzeltmek için başvurmama izin vererek formumda WPF kitaplıklarının neyi değiştirdiğini görmeyi kolaylaştırır (Spy ++ kullanarak).

Çok sayıda deneme yanılmadan sonra, denetimin kendisinde çift arabelleğe almayı etkinleştirmenin (yalnızca formun aksine) sorunu düzelttiğini fark ettim!


Bu nedenle, DoubleBuffering'i etkinleştirebilmeniz için DataGridView'e dayalı özel bir sınıf oluşturmanız yeterlidir. Bu kadar!

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

Tüm ızgara örneklerim bu özel sürümü kullandığı sürece, her şey yolunda. Bunun neden olduğu ve alt sınıf çözümünü kullanamadığım bir durumla karşılaşırsam (eğer koda sahip değilsem), sanırım bu denetimi forma enjekte etmeyi deneyebilirim :) ( 'I rağmen Bağımlılıktan bir kez daha kaçınmak için DoubleBuffered özelliğini dışarıdan zorlamak için yansıma kullanmayı deneme olasılığı daha yüksektir ).

Bu kadar önemsiz derecede basit bir şeyin zamanımın çoğunu tüketmesi üzücü ...


1
Multimon'un yüklü olduğu müşterilerle de benzer bir sorun yaşadık . Sebep ne olursa olsun, Multimon'u kapattıklarında sorun ortadan kalkar.
BlueRaja - Dany Pflughoeft

Bunun neden olduğunu ve neden DoubleBuffered'ın varsayılan olarak açılamadığını bilen ve açıklayabilen biri var mı?
Vojtěch Dohnal

Yanıtlar:


64

DoubleBuffering'i etkinleştirebilmeniz için DataGridView'e dayalı özel bir sınıf oluşturmanız yeterlidir. Bu kadar!


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

Tüm ızgara örneklerim bu özel sürümü kullandığı sürece, her şey yolunda. Bunun neden olduğu ve alt sınıf çözümünü kullanamadığım bir durumla karşılaşırsam (koda sahip değilsem), bu denetimi forma enjekte etmeyi deneyebilirim. Bağımlılıktan bir kez daha kaçınmak için DoubleBuffered özelliğini dışarıdan zorlamak için yansıma kullanmayı deneme olasılığı daha yüksektir).

Bu kadar önemsiz derecede basit bir şeyin zamanımın çoğunu tüketmesi üzücü ...

Not: Cevabı bir cevap haline getirmek, böylece soru cevaplandı olarak işaretlenebilir


1
Bunu WPF için Windows Forms Entegrasyonu ile nasıl yapabilirsiniz?
Kısmi

Cevap için teşekkürler. Bazen alt sınıf çözümünü nasıl kullanamazsınız? ("Koda sahip değilsem" bitini anlamadım).
Dan W

Fantastik! Hem tabloyu
doldururken

61

İşte Benoit'in önerdiği gibi alt sınıflandırma yapmadan yansıma kullanarak özelliği ayarlayan bazı kodlar.

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

3
Yardımcı olduğuma sevindim! Neredeyse göndermedim çünkü bu soru zaten bir yaşındaydı.
Brian Ensink

1
Neah, gelecekte bu ileti dizisini Google'dan yeni bulan belki de benim gibi birine her zaman yardımcı olacak. Teşekkürler! Btw, bunu Form1_Load bölümüne koymak tercih edilir mi?
Dan W

2
Sırf bunu bir fikir bulan birine vermek için: Bu, Controlsınıfta kullanışlı bir genişletme yöntemidir . public static void ToggleDoubleBuffered(this Control control, bool isDoubleBuffered).
Anthony

Veri ızgarası görünümünün yerleştirildiği FOrm'un yük olayına yerleştirilebilir mi?
Arie

18

VB.NET'te nasıl yapılacağını arayan kişiler için kod:

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

10

Önceki gönderilere ek olarak, Windows Forms uygulamaları için DataGridView bileşenlerini hızlandırmak için kullandığım şey bu. DrawingControl sınıfının kodu aşağıdadır.

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

Yapıcıda InitializeComponent () sonra DrawingControl.SetDoubleBuffered (control) arayın.

Büyük veri güncellemeleri yapmadan önce DrawingControl.SuspendDrawing (control) öğesini çağırın.

Büyük veri güncellemeleri yaptıktan sonra DrawingControl.ResumeDrawing (control) öğesini çağırın.

Bu son 2 en iyi bir dene / nihayet bloğu ile yapılır. (veya daha da iyisi , yapıcıda ve içinde sınıfı olarak yeniden yazın IDisposableve çağırın .)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();
    }
}

7

Bunun cevabı benim için de çalıştı. Çözümü uygulayan herkes için standart uygulama olması gerektiğini düşündüğüm bir iyileştirme ekleyeceğimi düşündüm.

Çözüm, özellikle kullanılabilir ağ bant genişliğinin düşük olduğu durumlarda, kullanıcı arabiriminin uzak masaüstü altında bir istemci oturumu olarak çalıştırıldığı durumlar dışında iyi çalışır. Böyle bir durumda, çift tamponlama kullanılarak performans daha da kötüleştirilebilir. Bu nedenle, aşağıdakileri daha eksiksiz bir cevap olarak öneriyorum:

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

Daha fazla ayrıntı için bkz.Uzak masaüstü bağlantısını algılama


1

Soruna bir çözüm buldum. Gelişmiş görüntü özelliklerinde sorun giderme sekmesine gidin ve donanım hızlandırma kaydırıcısını kontrol edin. Yeni şirket bilgisayarımı BT'den aldığımda, tam olarak bir tık olarak ayarlandı ve veri kılavuzlarıyla ilgili herhangi bir sorun yaşamadım. Ekran kartı sürücüsünü güncellediğimde ve tam olarak ayarladığımda, datagrid kontrollerinin boyanması çok yavaşladı. Bu yüzden onu olduğu yere sıfırladım ve sorun ortadan kalktı.

Umarım bu numara sizin için de işe yarar.


1

Sadece bu sorunu çözmek için yaptığımız şeyi eklemek için: En son Nvidia sürücülerine yükselttik sorunu çözdük. Hiçbir kodun yeniden yazılması gerekmedi.

Bütünlük açısından, kart Mart 2008 tarihli bir Nvidia Quadro NVS 290'dı (v. 169). En son sürüme yükseltme (Şubat 2009 tarihli 182 v.), Özellikle DataGridView olmak üzere tüm denetimlerim için boyama olaylarını önemli ölçüde iyileştirdi.

Bu sorun herhangi bir ATI kartında görülmedi (geliştirmenin gerçekleştiği yerde).


1

En iyi!:

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

Çift monitör sisteminde .NET 3.0 ve DataGridView kullanırken benzer bir sorunla karşılaştık.

Uygulamamız, ızgarayı, hücrelerin değiştirilemeyeceğini belirten gri bir arka planla görüntüleyecekti. Bir "ayarları değiştir" düğmesi seçildiğinde, program, kullanıcıya hücre metninin değiştirilebileceğini belirtmek için hücrelerin arka plan rengini beyaz değiştirir. Bir "iptal" düğmesi, yukarıda bahsedilen hücrelerin arka plan rengini tekrar griye çevirecektir.

Arka plan rengi değiştikçe, aynı sayıda satır ve sütuna sahip varsayılan boyutlu bir ızgaranın kısa bir izlenimi, bir titreme olacaktır. Bu sorun yalnızca birincil monitörde (ikincil monitörde asla) ortaya çıkmaz ve tek bir monitör sisteminde ortaya çıkmaz.

Yukarıdaki örneği kullanarak kontrolü iki kez ara belleğe almak sorunumuzu çözdü. Yardımınız için çok minnettarız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.