14

I'm making a program where I have a lot of panels and panels in panels.

I have a few custom drawn controls in these panels.

The resize function of 1 panel contains code to adjust the size and position of all controls in that panel.

Now as soon as I resize the program, the resize of this panel gets actived. This results in a lot of flickering of the components in this panel.

All user drawn controls are double buffered.

Can some one help me solve this problem?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
user575579
  • 141
  • 1
  • 1
  • 3
  • If I wasn't out of votes, I'd vote to close this as a duplicate of this question: [Winforms Double Buffering](http://stackoverflow.com/questions/3718380/winforms-double-buffering). So here it is for free. – Cody Gray - on strike Jan 14 '11 at 11:19
  • 3
    @Cody Gray - I dont agree! Question may be similar, but the answer provided under the link you sent is totally wrong/bad because it causes a lot side-effects. – HABJAN Jan 14 '11 at 11:51
  • @HABJAN: Can you tell me about some of those side effects? – Cody Gray - on strike Jan 14 '11 at 11:52
  • @Cody Gray: Tab control loses theme sometimes. (a same thing for some other controls) Windows Media player control freezes (it plays but it does not show movie playing). That is what i expirienced. – HABJAN Jan 14 '11 at 11:55
  • I have tried this solution (suggested by Cody Gray) prior of posting this, it wasn't working. That's why I started this new thread. Flickering of the controls inside the panel when resizing. I see them beeing resized, moved and painted one by one. – user575579 Jan 14 '11 at 12:00
  • 2
    The CreateParams overload works perfect on your project, no artifacts, all flicker gone. Too bad it doesn't work for you. – Hans Passant Jan 14 '11 at 15:57
  • Hans Passant: where should I add the CreateParams overload? – user575579 Jan 17 '11 at 12:56
  • @user575579: Add it to whichever form contains your controls. It overrides the [`CreateParams` property](http://msdn.microsoft.com/en-us/library/system.windows.forms.form.createparams.aspx) of the `Form`, which returns the creation parameters used to create the form's handle. – Cody Gray - on strike Jan 18 '11 at 04:42
  • possible duplicate of [Skinned C# form with huge black flicker while resizing!](http://stackoverflow.com/questions/318452/skinned-c-sharp-form-with-huge-black-flicker-while-resizing) – animuson Nov 24 '11 at 01:42

9 Answers9

24

To get rid of the flicker while resizing the win form, suspend the layout while resizing. Override the forms resizebegin/resizeend methods as below.

protected override void OnResizeBegin(EventArgs e) {
    SuspendLayout();
    base.OnResizeBegin(e);
}
protected override void OnResizeEnd(EventArgs e) {
    ResumeLayout();
    base.OnResizeEnd(e);
}

This will leave the controls intact (as they where before resizing) and force a redraw when the resize operation is completed.

Dan
  • 778
  • 7
  • 18
  • Background image now flicks little, but stills flicks. – Sorry IwontTell Jun 05 '20 at 09:58
  • I get a Stack Overflow exception when resizing with this code. – TK-421 Aug 21 '20 at 09:31
  • @TK-421 did you remember to call base? What stacktrace to you get? Where is the loop? Try to identify it and break it. – Dan Aug 31 '20 at 04:53
  • @Dan it works without base.OnResizeBegin(e), because I have actually put this code inside Form1_ResizeBegin event, not in override. – TK-421 Aug 31 '20 at 06:20
  • @TK-421, that's why you get a stack overflow exception. If you use the code in the example it will work. What you have done is implement an event handler for the ResizeBegin event, which is called by the OnResizeBegin function, and when you in the ResizeBegin event, call the OnResizeBegin function, you will trigger the event again causing the circular recursion and the resulting stack overflow. – Dan Sep 04 '20 at 10:56
18

Looking at the project you posted, the flickering is really bad when you have the first tab selected, with the gradient-filled group boxes. With the second or third tab showing, there's hardly any flicker, if at all.

  Your main form

So clearly the problem has something to do with the controls you're showing on that tab page. A quick glance at the code for the custom gradient-filled group box class gives away the more specific cause. You're doing a lot of really expensive processing each time you draw one of the groupbox controls. Because each groupbox control has to repaint itself each time the form is resized, that code is getting executed an unbelievable number of times.

Plus, you have the controls' background set to "Transparent", which has to be faked in WinForms by asking the parent window to draw itself first inside the control window to produce the background pixels. The control then draws itself on top of that. This is also more work than filling the control's background with a solid color like SystemColors.Control, and it's causing you to see the form's pixels being drawn while you resize the form, before the groupboxes have a chance to paint themselves.

Here's the specific code I'm talking about from your custom gradient-filled groupbox control class:

protected override void OnPaint(PaintEventArgs e)
{
    if (Visible)
    {
        Graphics gr = e.Graphics;
        Rectangle clipRectangle = new Rectangle(new Point(0, 0), this.Size);
        Size tSize = TextRenderer.MeasureText(Text, this.Font);
        Rectangle r1 = new Rectangle(0, (tSize.Height / 2), Width - 2, Height - tSize.Height / 2 - 2);
        Rectangle r2 = new Rectangle(0, 0, Width, Height);
        Rectangle textRect = new Rectangle(6, 0, tSize.Width, tSize.Height);

        GraphicsPath gp = new GraphicsPath();
        gp.AddRectangle(r2);
        gp.AddRectangle(r1);
        gp.FillMode = FillMode.Alternate;

        gr.FillRectangle(new SolidBrush(Parent.BackColor), clipRectangle);

        LinearGradientBrush gradBrush;
        gradBrush = new LinearGradientBrush(clipRectangle, SystemColors.GradientInactiveCaption, SystemColors.InactiveCaptionText, LinearGradientMode.BackwardDiagonal);
        gr.FillPath(gradBrush, RoundedRectangle.Create(r1, 7));

        Pen borderPen = new Pen(BorderColor);
        gr.DrawPath(borderPen, RoundedRectangle.Create(r1, 7));
        gr.FillRectangle(gradBrush, textRect);
        gr.DrawRectangle(borderPen, textRect);
        gr.DrawString(Text, base.Font, new SolidBrush(ForeColor), 6, 0);
    }

}

protected override void OnPaintBackground(PaintEventArgs pevent)
{
    if (this.BackColor == Color.Transparent)
        base.OnPaintBackground(pevent);
}

And now that you've seen the code, red warning flags ought to go up. You're creating a bunch of GDI+ objects (brushes, pens, regions, etc.), but failing to Dispose any of them! Almost all of that code should be wrapped in using statements. That's just sloppy coding.

Doing all of that work costs something. When the computer is forced to devote so much time to rendering controls, other things lag behind. You see a flicker as it strains to keep up with the resize. It's no different than anything else that overloads a computer (like a computing the value of pi), it's just really easy to do so when you use as many custom drawn controls like you do here. Transparency is hard in Win32, and so is a lot of custom 3D painting. It makes the UI look and feel clunky to the user. Yet another reason that I don't understand the rush away from native controls.

You really only have three options:

  1. Deal with the flicker. (I agree, this is not a good option.)
  2. Use different controls, like the standard, built-in ones. Sure, they may not have a fancy gradient effect, but that's going to look broken half of the time anyway if the user has customized their Windows theme. It's also reasonably hard to read black text on a dark gray background.
  3. Change the painting code within your custom controls to do less work. You may be able to get by with some simple "optimizations" that don't cost you any of the visual effects, but I suspect this is unlikely. It's a tradeoff between speed and eye candy. Doing nothing is always faster.
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • I get your point, but I've used the GradientGroupBox in other programs in an equally extensive way with significantly less problems. Have a look at this program to compare it. I know I'm doing something wrong in this code, but I can't put my finger on it. http://dl.dropbox.com/u/18687229/Combo%20Tuning.zip PS: I'm not familiar with the difference between drawing with the using statement and the way I did it. Do you have some info about this? – user575579 Jan 15 '11 at 14:05
  • @user575579: First off, please don't post executables. If I didn't have a virtual machine set up, I wouldn't run that. It's not that I don't trust you, but I don't trust you. ;-) Second, I really don't understand the difference between the two projects...That one flickers just as much as the first one to my eyes. The drawing is really laggy when you resize the form. Neither is *unusable*, but at the same time, neither is fluid. Third, because you *didn't* post the code for the second project, I really can't tell you what you're doing differently code-wise. I suspect there's some reason that... – Cody Gray - on strike Jan 16 '11 at 08:27
  • the second is putting less pressure on the garbage collector than the first, but that's just a guess. Fourth and finally, the `using` statement isn't terribly complicated. All it does is ensure that an object's `Dispose` method is called whenever you're finished using it. Here's an MSDN reference with code samples: http://msdn.microsoft.com/en-us/library/yh598w02.aspx – Cody Gray - on strike Jan 16 '11 at 08:28
3

I successfully eliminate flicker when form resize using this code. Thanks.

VB.NET

Public Class Form1

Public Sub New()
    Me.SetStyle(ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.SupportsTransparentBackColor, True)
End Sub

Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
    Me.Update()
End Sub

End Class

C#

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        Resize += Form1_Resize;
        this.SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.SupportsTransparentBackColor, true);
    }

    private void Form1_Resize(object sender, System.EventArgs e)
    {
        this.Update();
    }

}
new bie
  • 2,745
  • 6
  • 24
  • 26
  • I had the same problem, and your solution works for me, but now I have another problem with speed of form resizing - very slow. I have few controls on the form like buttons, tabControl, images, listbox, datagridview... Any sugestions what is the reason of that slowdown? – syp_dino Jan 28 '14 at 14:30
3

So I ran into this same problem - my control with a transparent background was repainting like 34 times, and what worked for me was:

On my form that contained the control

protected override void OnResize(EventArgs e)
    {
        myControl.Visible = false;
        base.OnResize(e);
        myControl.Visible = true;
    }

And the same in the control:

protected override void OnResize(EventArgs e)
    {
        this.Visible = false;
        base.OnResize(e);
        this.Visible = true;
    }

This reduced the amount of repainting to 4, which effectively eliminated any flicker when the control was being resized.

ChandlerPelhams
  • 1,648
  • 4
  • 38
  • 56
1

Maybe a good solution for you will be to use Form.ResizeBegin and Form.ResizeEnd events.

On ResizeBegin set main panel visibility to false, on ResizeEnd set main panel visibility to true.

This way panels will not be redrawn while someone is resizing your form.

HABJAN
  • 9,212
  • 3
  • 35
  • 59
1

While hooking into ResizeBegin and ResizeEnd is the right idea, instead of hiding the main panel's visibility I'd instead delay any resize calculations until ResizeEnd. In this case, you don't even need to hook into ResizeBegin or Resize - all the logic goes into ResizeEnd.

I say this for two reasons. One, even though the panel is hidden, the resize operation will likely still be expensive and so the form won't feel as responsive as it should unless the resize calculations are delayed. Two, hiding the pane's contents while resizing can be jarring to the user.

bsegraves
  • 980
  • 13
  • 22
0

I had the same problem.

It seams that this is happening because you are using rounded corners. When I set CornerRadius property to 0, the flickering was gone.

So far I have only found the following workaround. Not the nicest one, but it stops the flickering.

private void Form_ResizeBegin(object sender, EventArgs e)
{
  rectangleShape.CornerRadius = 0;
}

private void Form_ResizeEnd(object sender, EventArgs e)
{
  rectangleShape.CornerRadius = 15;
}
Mumblic
  • 105
  • 2
  • 8
0

I Had A Similar Issue Like This. My Entire Form Was Resizing Slowly And The Controls Painted Them In An Ugly Manner. So This Helped Me:

//I Added This To The Designer File, You Can Still Change The WindowState In Designer View If You Want. This Helped Me Though.
this.WindowState = FormWindowState.Maximized;

And In The Resize Event, Add This Code To The Beginning

this.Refresh();
Tridib Roy Arjo
  • 26
  • 2
  • 10
0

Flickering has nothing to do with suspending layout, resuming layout, resize, refresh, or whatever listed here as an answer. To make less flickering, just make sure controls are created in the panel as container, not directly to the form. When controls are created directly in the form without container, the form will flicker as many times as the number of those controlls. If not (created in the containing panel), form will flicker less. When the form is resized (not only by _Resized event handler but also by code), the form will flicker only as many times as the number of panels in the form plus the number of all the controls directly added to the form.

Regarding the question, I believe the main cause of the flickering is due to the resizing controlls in the panel by code, which causes all the other panels size and the all controlls in the panels. If we set the anchors for the panel and set the anchors for the controlls in the panel, there is no need to adjust panel size or controlls' size in the panel by code. Anchor of the control in the panel is to the panel. Anchor of the panel in the panel is to the containing panel. There is no need to resize controls in the panel if we apply panels and anchors properly in the form.

Brian
  • 11
  • 2