Eu tenho um controle que eu tenho que fazer grandes modificações. Eu gostaria de impedi-lo completamente de redesenhar enquanto faço isso - SuspendLayout e ResumeLayout não são suficientes. Como suspiro a pintura de um controle e de seus filhos?
184
Respostas:
No meu trabalho anterior, lutamos para fazer com que nosso aplicativo de interface do usuário rico pintasse instantaneamente e sem problemas. Estávamos usando controles .Net padrão, controles personalizados e controles DevExpress.
Depois de muito uso no Google e no refletor, me deparei com a mensagem win32 do WM_SETREDRAW. Isso realmente interrompe o desenho dos controles enquanto você os atualiza e pode ser aplicado, IIRC ao painel pai / contendo.
Esta é uma classe muito, muito simples, demonstrando como usar esta mensagem:
Há discussões mais completas sobre isso - google for C # e WM_SETREDRAW, por exemplo
Tremulação C #
Suspender layouts
E para quem possa interessar, este é um exemplo semelhante no VB:
fonte
Control
classe base de todos os controles WinForms já faz para os métodosBeginUpdate
eEndUpdate
. Enviar a mensagem você mesmo não é melhor do que usar esses métodos para fazer o trabalho pesado para você e certamente não pode produzir resultados diferentes.Control.Handle
forçará a criação do identificador da janela e poderá afetar o desempenho. Por exemplo, se você estava movendo um controle em um formulário antes que ele fosse exibido, se você chamar issoSuspendDrawing
antes, sua movimentação será mais lenta. Provavelmente deve terif (!parent.IsHandleCreated) return
verificações nos dois métodos.A seguir está a mesma solução do ng5000, mas não usa P / Invoke.
fonte
Message
e ondeNativeWindow
estão; pesquisar na documentação por uma classe chamadaMessage
não é realmente tão divertido.Invalidate()
não funciona tão bem quanto, aRefresh()
menos que seja seguido por um de qualquer maneira.Normalmente, uso uma versão modificada da resposta do ngLink .
Isso permite que as chamadas de suspensão / retomada sejam aninhadas. Você deve certificar-se de combinar cada
SuspendDrawing
um com umResumeDrawing
. Portanto, provavelmente não seria uma boa ideia divulgá-las.fonte
SuspendDrawing(); try { DrawSomething(); } finally { ResumeDrawing(); }
. Outra opção é implementar isso em umaIDisposable
classe e incluir a parte do desenho em umausing
declaração. O identificador seria passado para o construtor, o que suspenderia o desenho.DllImport
declarawParam
comobool
?Para ajudar a não esquecer de reativar o desenho:
uso:
fonte
action()
lança uma exceção? (Utilize um try / finally)Uma boa solução sem usar interoperabilidade:
Como sempre, basta ativar DoubleBuffered = true no seu CustomControl. Em seguida, se você tiver algum contêiner como FlowLayoutPanel ou TableLayoutPanel, obtenha uma classe de cada um desses tipos e nos construtores, ative o buffer duplo. Agora, basta usar seus contêineres derivados em vez dos contêineres Windows.Forms.
fonte
Com base na resposta do ng5000, gosto de usar esta extensão:
Usar:
fonte
Aqui está uma combinação de ceztko e ng5000 para trazer uma versão de extensões VB que não usa pinvoke
fonte
Eu sei que essa é uma pergunta antiga, já respondida, mas aqui está minha opinião sobre isso; Refatorei a suspensão das atualizações em um IDisposable - dessa forma, posso incluir as instruções que desejo executar em uma
using
instrução.fonte
Isso é ainda mais simples e talvez hacky - como eu posso ver muitos dos músculos GDI nesse segmento , e obviamente é apenas uma boa opção para determinados cenários. YMMV
No meu cenário, uso o que chamarei de UserControl "pai" - e, durante o
Load
evento, simplesmente removo o controle a ser manipulado da.Controls
coleção dos pais e as informações do pai.OnPaint
cuidam da pintura completa da criança. controle de qualquer maneira especial. desligue totalmente os recursos de pintura da criança.Agora, passo a rotina de pintura do meu filho para um método de extensão baseado neste conceito de Mike Gold para imprimir formulários do Windows .
Aqui estou precisando de um subconjunto de rótulos para renderizar perpendicularmente ao layout:
Em seguida, isento o controle filho de ser pintado, com este código no
ParentUserControl.Load
manipulador de eventos:Em seguida, no mesmo ParentUserControl, pintamos o controle a ser manipulado desde o início:
Depois de hospedar o ParentUserControl em algum lugar, por exemplo, um Windows Form - eu estou achando que meu Visual Studio 2015 renderiza o formulário corretamente no Design Time e no tempo de execução:
Agora, como minha manipulação específica gira o controle infantil em 90 graus, tenho certeza de que todos os pontos de acesso e interatividade foram destruídos naquela região - mas o problema que resolvi foi para uma etiqueta de embalagem que precisava visualizar e imprimir, o que funcionou bem para mim.
Se existem maneiras de reintroduzir os pontos quentes e o controle no meu controle órfão proposital - eu adoraria aprender sobre isso algum dia (não para esse cenário, é claro, mas apenas para aprender). Claro, o WPF suporta tanta loucura OOTB .. mas ... ei ... WinForms ainda é muito divertido, certo?
fonte
Ou apenas use
Control.SuspendLayout()
eControl.ResumeLayout()
.fonte