Pergunta

Eu realmente resolvido isso, mas eu estou postando-lo para a posteridade.

Eu corri em um problema muito estranho com o DataGridView no meu sistema dual-monitor. A questão se manifesta como um repaint extremamente lento do controle ( como 30 segundos para um repaint completo ), mas somente quando ele está em uma das minhas telas. Quando, por outro, a velocidade repaint é bom.

Eu tenho uma 8800 GT da Nvidia com os últimos drivers não-beta (175. algo). É um bug de driver? Vou deixar isso no ar, uma vez que eu tenho que viver com esta configuração particular. (Não acontece em placas ATI, embora ...)

A velocidade de pintura não tem nada a ver com o conteúdo da célula, e desenho personalizado não melhora o desempenho em tudo -. Mesmo quando apenas pintar um retângulo sólido

Mais tarde eu descobrir que a colocação de um ElementHost (a partir do namespace System.Windows.Forms.Integration) no formulário corrige o problema. Ele não tem que ser sujado com; ele só precisa ser um filho de forma a DataGridView também está ligado. Ele pode ser redimensionado para (0, 0), enquanto o Visível propriedade é verdadeira.

Eu não quero adicionar explicitamente a dependência .NET 3 / 3,5 ao meu pedido; Eu faço um método para criar esse controle em tempo de execução (se possível) usando a reflexão. Ele funciona, e pelo menos ele falhar graciosamente em máquinas que não têm a biblioteca necessária -. Ele só vai voltar a ser lenta

Este método também permite que me aplicar a correção, enquanto o aplicativo está sendo executado, tornando mais fácil para ver o que as bibliotecas WPF estão mudando no meu formulário (usando Spy ++).

Depois de muita tentativa e erro, eu aviso que permite o buffer duplo no próprio controle (em oposição a apenas o formulário) corrige o problema!


Assim, você só precisa fazer uma classe personalizada baseada fora de DataGridView para que possa permitir a sua DoubleBuffering. É isso aí!

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

Enquanto todos os meus instâncias da grade estão usando esta versão personalizada, tudo está bem. Se eu nunca correr em uma situação causada por esta onde eu não sou capaz de usar a solução subclasse (se eu não tenho o código), suponho que eu poderia tentar injetar que o controle sobre a forma :) ( embora eu vou ser mais propensos a tentar usar a reflexão para forçar a propriedade DoubleBuffered na do lado de fora para mais uma vez evitar a dependência ).

É triste que tal coisa trivialmente simples comeu muito do meu tempo ...

Foi útil?

Solução

Você só precisa fazer uma classe personalizada baseada fora de DataGridView para que possa permitir a sua DoubleBuffering. É isso aí!


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

Enquanto todos os meus instâncias da grade estão usando esta versão personalizada, tudo está bem. Se eu nunca correr em uma situação causada por esta onde eu não sou capaz de usar a solução subclasse (se eu não tenho o código), suponho que eu poderia tentar injetar que o controle sobre a forma :) (embora eu' vai ser mais propensos a tentar usar a reflexão para forçar a propriedade DoubleBuffered na do lado de fora para mais uma vez evitar a dependência).

É triste que tal coisa trivialmente simples comeu muito do meu tempo ...

Nota: Fazendo a resposta uma resposta então a questão pode ser marcada como respondida

Outras dicas

Aqui está um código que define a propriedade usando a reflexão, sem subclassificação como Benoit sugere.

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

Para pessoas procurando como fazer isso em VB.NET, aqui está o código:

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

Adicionando a posts anteriores, para aplicações Windows Forms isso é o que eu uso para componentes DataGridView para torná-los rapidamente. O código para o drawingControl classe está abaixo.

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

Chamada DrawingControl.SetDoubleBuffered (controle) após InitializeComponent () no construtor.

Chamada DrawingControl.SuspendDrawing (controle) antes de fazer atualizações de dados grandes.

Chamada DrawingControl.ResumeDrawing (controle) depois de fazer atualizações de dados grandes.

Estes últimos 2 são melhor feito com a / bloco try, finalmente. (Ou melhor ainda reescrever a classe como IDisposable e SuspendDrawing() chamada no construtor e ResumeDrawing() em 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();
    }
}

A resposta para isso funcionou para mim também. Eu pensei que eu gostaria de acrescentar um refinamento que eu acho que deve ser uma prática padrão para qualquer implementação da solução.

A solução funciona bem, exceto quando a interface do usuário está sendo executado como uma sessão de cliente sob desktop remoto, especialmente onde a largura de banda disponível é baixa. Em tal caso, um, o desempenho pode ser agravada pelo uso de buffer duplo. Portanto, sugiro o seguinte como uma resposta mais completa:

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

Para mais detalhes, consulte a Detecção de conexão de desktop remoto

Eu encontrei uma solução para o problema. Vá para a guia solução de problemas nas propriedades avançadas de visualização e verificar o controle deslizante aceleração de hardware. Quando eu tenho o meu novo PC empresa de TI, foi definida como um carrapato de cheio e eu não tiver qualquer problema com datagrids. Uma vez que eu atualizei o driver da placa de vídeo e configurá-lo para cheia, pintura de controles datagrid tornou-se muito lento. Então eu redefini-la de volta para onde estava eo problema foi embora.

Hope este truque funciona para você também.

Só para acrescentar o que fizemos para corrigir esse problema: Nós atualizado com os últimos drivers da Nvidia resolveu o problema. Nenhum código teve que ser reescrito.

Para completar, o cartão foi uma Nvidia Quadro NVS 290 com os drivers datado de Março de 2008 (v. 169). A atualização para a mais recente (v. 182 datado Fev 2009) melhorou significativamente os eventos de pintura para todos os meus controles, especialmente para o DataGridView.

Esta questão não foi visto em nenhum placas ATI (onde ocorre o desenvolvimento).

Melhor:

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

Nós já experimentou um problema semelhante usando .NET 3.0 e DataGridView em um sistema de monitor duplo.

O aplicativo poderia exibir a grade com um fundo cinza, indicando que as células não poderia ser alterado. Ao selecionar um botão "alterar as configurações", o programa iria mudar a cor das células brancas para indicar ao usuário que o texto da célula poderia ser mudado fundo. Um botão "cancelar" mudaria a cor das células acima mencionadas de fundo para cinza.

Como a cor de fundo mudou haveria um lampejo, uma breve impressão de um padrão de tamanho de grade com o mesmo número de linhas e colunas. Este problema só iria ocorrer no monitor primário (nunca o secundário) e não poderia ocorrer em um único sistema monitor.

buffer duplo de controle, usando o exemplo acima, resolveu nosso problema. Nós muito apreciado sua ajuda.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top