Como consertar a cintilação nos controles do usuário

Na minha aplicação, estou constantemente mudando de um controle para outro. Eu criei não. de controles de usuário, mas durante a navegação meus controles ficam com cintilação. leva 1 ou 2 segundos para atualizar. Eu tentei definir isso

SetStyle(ControlStyles.OptimizedDoubleBuffer, true); or SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.DoubleBuffer, true); 

mas não ajudou … Cada controle tem a mesma imagem de fundo com diferentes controles. Então, qual é a solução para isso ..
Obrigado.

Não é o tipo de tremulação que o buffer duplo pode resolver. Nem BeginUpdate ou SuspendLayout. Você tem muitos controles, o BackgroundImage pode piorar muito .

Começa quando o UserControl se pinta. Ele desenha o BackgroundImage, deixando buracos onde as janelas de controle filho vão. Cada controle de criança, em seguida, recebe uma mensagem para pintar-se, eles vão preencher o buraco com o conteúdo da janela. Quando você tem muitos controles, esses buracos ficam visíveis para o usuário por um tempo. Eles normalmente são brancos, contrastando mal com o BackgroundImage quando está escuro. Ou podem ser pretos se o formulário tiver seu conjunto de propriedades Opacidade ou Transparência, contrastando mal com praticamente qualquer coisa.

Esta é uma limitação bastante fundamental do Windows Forms, ele está preso à forma como o Windows renderiza janelas. Corrigido pelo WPF btw, ele não usa janelas para controles filho. O que você deseja é o buffer duplo de todo o formulário, incluindo os controles filhos. Isso é possível, verifique meu código neste segmento para a solução. Ele tem efeitos colaterais e não aumenta a velocidade da pintura. O código é simples, cole isso no seu formulário (não no controle do usuário):

 protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED return cp; } } 

Há muitas coisas que você pode fazer para melhorar a velocidade da pintura, a tal ponto que a cintilação não é mais perceptível. Comece atacando o BackgroundImage. Eles podem ser realmente caros quando a imagem de origem é grande e precisa ser reduzida para caber no controle. Altere a propriedade BackgroundImageLayout para “Tile”. Se isso causar uma aceleração perceptível, volte ao seu programa de pintura e redimensione a imagem para obter uma melhor correspondência com o tamanho de controle típico. Ou escreva código no método OnResize () da UC para criar uma cópia da imagem adequadamente dimensionada para que ela não precise ser redimensionada toda vez que o controle for repintado. Use o formato de pixel Format32bppPArgb para essa cópia, que renderiza cerca de 10 vezes mais rápido do que qualquer outro formato de pixel.

A próxima coisa que você pode fazer é evitar que os buracos sejam tão perceptíveis e contrastem mal com a imagem. Você pode desativar o sinalizador de estilo WS_CLIPCHILDREN para o UC, o sinalizador que impede a UC de pintura na área onde os controles filho vão. Cole este código no código do UserControl:

 protected override CreateParams CreateParams { get { var parms = base.CreateParams; parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN return parms; } } 

Os controles filhos agora serão pintados na parte superior da imagem de plano de fundo. Você ainda pode vê-los se pintando um por um, mas o feio branco ou buraco negro intermediário não será visível.

Por último, mas não menos importante, reduzir o número de controles filhos é sempre uma boa abordagem para resolver problemas lentos de pintura. Substitua o evento OnPaint () da UC e desenhe o que agora é mostrado em uma criança. Particular Label e PictureBox são muito inúteis. Conveniente para apontar e clicar, mas sua alternativa leve (desenhando uma seqüência de caracteres ou uma imagem) leva apenas uma única linha de código no seu método OnPaint ().

Este é um problema real, e a resposta que Hans Passant deu é ótima para salvar o flicker. No entanto, existem efeitos colaterais, como ele mencionou, e eles podem ser feios (UI feia). Como afirmado, “Você pode desativar o sinalizador de estilo WS_CLIPCHILDREN para o UC”, mas isso só o desativa para uma UC. Os componentes no formulário principal ainda apresentam problemas.

Exemplo, uma barra de rolagem do painel não pinta, porque está tecnicamente na área filha. No entanto, o componente filho não desenha a barra de rolagem, para que ele não seja pintado até que o mouse acabe (ou outro evento o aciona).

Além disso, os icons animados (alterando os icons em um loop de espera) não funcionam. Remover icons em uma tabPage.ImageKey não redimensiona / repinta as outras tabPages adequadamente.

Então, eu estava procurando uma maneira de desativar o WS_CLIPCHILDREN na pintura inicial para que o meu formulário fosse carregado de maneira agradável ou, melhor ainda, apenas ativá-lo enquanto redimensionava meu formulário com muitos componentes.

O truque é fazer com que o aplicativo chame CreateParams com o estilo WS_EX_COMPOSITED / WS_CLIPCHILDREN desejado? Eu encontrei um hack aqui ( http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx ) e funciona bem. Obrigado AngryHacker!

Eu coloquei a chamada TurnOnFormLevelDoubleBuffering () no evento ResizeBegin. TurnOffFormLevelDoubleBuffering () chama o evento ResizeEnd do formulário (ou apenas o deixa WS_CLIPCHILDREN depois que ele é pintado corretamente corretamente.)

  int originalExStyle = -1; bool enableFormLevelDoubleBuffering = true; protected override CreateParams CreateParams { get { if (originalExStyle == -1) originalExStyle = base.CreateParams.ExStyle; CreateParams cp = base.CreateParams; if (enableFormLevelDoubleBuffering) cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED else cp.ExStyle = originalExStyle; return cp; } } public void TurnOffFormLevelDoubleBuffering() { enableFormLevelDoubleBuffering = false; this.MaximizeBox = true; } 

Se você estiver fazendo alguma pintura personalizada no controle (ou seja, substituindo OnPaint), você mesmo pode tentar o buffer duplo.

 Image image; protected override OnPaint(...) { if (image == null || needRepaint) { image = new Bitmap(Width, Height); using (Graphics g = Graphics.FromImage(image)) { // do any painting in image instead of control } needRepaint = false; } e.Graphics.DrawImage(image, 0, 0); } 

E invalidar seu controle com uma propriedade NeedRepaint

Caso contrário, a resposta acima com SuspendLayout e ResumeLayout é provavelmente o que você deseja.

Tente os methods BeginUpdate / EndUpdate OU SuspendLayout / ResumeLayout. Veja a seguir
Como corrigir problemas de cintilação do controle winform nesteds
Piscando durante as atualizações para controles em WinForms (por exemplo, DataGridView)

No formulário principal ou no controle de usuário em que a imagem de segundo plano reside, defina a propriedade BackgroundImageLayout como Center ou Stretch . Você notará uma grande diferença quando o controle do usuário estiver sendo renderizado.

Eu tentei adicionar isso como um comentário, mas eu não tenho pontos suficientes. Esta é a única coisa que ajudou meus problemas de tremulação, muito obrigado a Hans por seu post. Para qualquer um que esteja usando o construtor c ++ como eu, aqui está a tradução

Adicione a declaração CreateParams ao arquivo .h do formulário principal do seu aplicativo, por exemplo,

 class TYourMainFrom : public TForm { protected: virtual void __fastcall CreateParams(TCreateParams &Params); } 

e adicione isso ao seu arquivo .cpp

 void __fastcall TYourMainForm::CreateParams(TCreateParams &Params) { Params.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED TForm::CreateParams(Params); } 

Coloque o código abaixo em seu construtor ou evento OnLoad e se você estiver usando algum tipo de controle de usuário personalizado que tenha subcontroles, você precisará certificar-se de que esses controles personalizados também sejam buffer duplos (mesmo que na documentação do MS eles digam está definido para true por padrão).

Se você estiver fazendo um controle personalizado, talvez queira adicionar esse sinalizador ao seu ctor:

 SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 

Opcionalmente, você pode usar este código no seu formulário / controle:

 foreach (Control control in Controls) { typeof(Control).InvokeMember("DoubleBuffered", BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, control, new object[] { true }); } 

Fazemos a iteração de todos os controles no formulário / controle e DoubleBuffered sua propriedade DoubleBuffered e, em seguida, DoubleBuffered para true a fim de tornar cada controle no formulário double buffered. A razão pela qual fazemos reflexo aqui, é porque imagine que você tem um controle que tem controles filhos que não são acessíveis, assim, mesmo que sejam controles privados, ainda mudaremos sua propriedade para true.

Mais informações sobre a técnica de buffer duplo podem ser encontradas aqui .

Há outra propriedade que eu geralmente substituo para resolver esse problema:

 protected override CreateParams CreateParams { get { CreateParams parms = base.CreateParams; parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED return parms; } } 

WS_EX_COMPOSITED – Pinta todos os descendentes de uma janela na ordem de pintura de baixo para cima usando o buffer duplo.

Você pode encontrar mais dessas bandeiras de estilo aqui .

Espero que ajude!

Apenas para adicionar a resposta que Hans deu:

(Versão TLDR: a transparência é mais pesada do que você pensa, use apenas colors sólidas em todos os lugares)

Se WS_EX_COMPOSITED, DoubleBuffered e WS_CLIPCHILDREN não resolveram o seu flicker (para mim o WS_CLIPCHILDREN tornou ainda pior), tente isto: passe por TODOS os seus controles e todo o seu código, e onde quer que você tenha Transparência ou semi-transparência para BackColor, ForeColor ou qualquer outra cor, basta removê-lo, use apenas colors sólidas. Na maioria dos casos em que você acha que precisa apenas usar transparência, você não precisa . Reprojetar seu código e controles e use colors sólidas. Eu tive uma terrível e terrível oscilação e o programa estava lento. Uma vez que eu removi a transparência, ela acelerou significativamente e há 0 cintilação.

EDIT: Para adicionar mais, acabei de descobrir que WS_EX_COMPOSITED não tem que ser em toda a janela, poderia ser aplicado apenas para controles específicos! Isso me poupou muitos problemas. Basta criar um controle personalizado herdado de qualquer controle necessário e colar a substituição já postada para WS_EX_COMPOSITED. Dessa forma, você obtém buffer duplo de baixo nível somente nesse controle, evitando os efeitos colaterais desagradáveis ​​no restante do aplicativo!

Eu sei que esta pergunta é muito antiga, mas quero dar a minha experiência sobre isso.

Eu tive um monte de problemas com Tabcontrol cintilando em uma forma com OnPaint substituído e / ou OnPaintBackGround no Windows 8 usando o .NET 4.0.

O único pensamento que funcionou foi NÃO USE o método Graphics.DrawImage no OnPaint substitui, em outras palavras, quando o desenho foi feito diretamente para os charts fornecidos pelo PaintEventArgs , mesmo pintando todo o retângulo, o cintilante desapareceu. Mas se chamar o método DrawImage , mesmo desenhando um Bitmap cortado, (criado para buffer duplo), o flicker aparecerá.

Espero que ajude!

Eu combinei essa correção de cintilação e essa correção de fonte , então eu tive que adicionar um pouco do meu próprio código para iniciar um timer na pintura para invalidar o TabControl quando ele sai da canvas e volta, etc.

Todos os três fazem isso:

 using System; using System.Runtime.InteropServices; using System.Windows.Forms; public class TabControlEx:TabControl { [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); private const int WM_PAINT = 0x0f; private const int WM_SETFONT = 0x30; private const int WM_FONTCHANGE = 0x1d; private System.Drawing.Bitmap buffer; private Timer timer = new Timer(); public TabControlEx() { timer.Interval = 1; timer.Tick += timer_Tick; this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); } void timer_Tick(object sender, EventArgs e) { this.Invalidate(); this.Update(); timer.Stop(); } protected override void WndProc(ref Message m) { if (m.Msg == WM_PAINT) timer.Start(); base.WndProc(ref m); } protected override void OnPaint(PaintEventArgs pevent) { this.SetStyle(ControlStyles.UserPaint, false); base.OnPaint(pevent); System.Drawing.Rectangle o = pevent.ClipRectangle; System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control); if (o.Width > 0 && o.Height > 0) DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height)); pevent.Graphics.DrawImageUnscaled(buffer, 0, 0); this.SetStyle(ControlStyles.UserPaint, true); } protected override void OnResize(EventArgs e) { base.OnResize(e); buffer = new System.Drawing.Bitmap(Width, Height); } protected override void OnCreateControl() { base.OnCreateControl(); this.OnFontChanged(EventArgs.Empty); } protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); IntPtr hFont = this.Font.ToHfont(); SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1)); SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero); this.UpdateStyles(); } } 

Eu não sou o criador, mas pelo que entendi o bitmap faz todo o bug ignorando.

Esta foi a única coisa que resolveu definitivamente o TabControl (com Icons) para mim.

vídeo resultado diferença: tabcontrol van tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

ps. você precisará definir HotTrack = true, porque isso corrige esse bug também

Você tentou Control.DoubleBuffered Property?

Obtém ou define um valor indicando se esse controle deve redesenhar sua superfície usando um buffer secundário para reduzir ou evitar o flicker.

Também isso e isso pode ajudar.

Não há necessidade de qualquer buffer duplo e todas essas coisas …

Uma solução simples …

Se você estiver usando a interface MDI, basta colar o código abaixo no formulário principal. Isso removerá todas as cintilações das páginas. No entanto, algumas páginas que requerem mais tempo para carregar aparecerão em 1 ou 2 segundos. Mas isso é melhor do que mostrar uma página bruxuleante em que cada item vem um por um.

Essa é a única melhor solução para todo o aplicativo. Veja o código para colocar no formulário principal:

 protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED return cp; } }