Reducing Flicker in C# Windows Forms

Experts,

I know this has been discussed a lot online, but I chose to ask this question here anyway. Frankly speaking I`ve had great luck with the suggestions here on Experts Exchange.

So now the question .. How do I eliminate / reduce flicker in C# Windows Forms / UserControls? I have a great application but the flicker is just too annoying and hard to ignore. I think the design is pretty simple. I use layered User Controls and Panel. Example : Panel - UserControl - UserControl.  Its only 3 layers deep maybe 4 at the max. I`m using a gradient panel. Online discussions advice to use DoubleBuffering. I`ve tried Suspend Layout and Resume Layout. I still have flickering.

Any suggestions, or help would be greatly appreciated.
protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x02000000;
                return cp;
            }
        }

Open in new window

public GradientPanel()
		{
			this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
			this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
			this.SetStyle(ControlStyles.UserPaint, true);
			this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
			InitializeComponent();
		}

Open in new window

San24Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Alfred A.Commented:
Try this out:




public GradientPanel(
{
     this.SetStyle(ControlStyles.OptimizedDoubleBuffer | 
                        ControlStyles.UserPaint |
                        ControlStyles.AllPaintingInWmPaint, true);
     this.SetStyle(ControlStyles.ResizeRedraw, true);		
     this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
     InitializeComponent();
}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
San24Author Commented:
Alfred1 - I already tried that. Doesn`t seem to make any difference. The flicker is still present.
0
Alfred A.Commented:
OK.  Check this link as well.  There are alternatives to the use of the built-in double buffer.

http://www.codeproject.com/KB/graphics/DoubleBuffering.aspx
0
Cloud Class® Course: Microsoft Windows 7 Basic

This introductory course to Windows 7 environment will teach you about working with the Windows operating system. You will learn about basic functions including start menu; the desktop; managing files, folders, and libraries.

Alfred A.Commented:
Oh by the way, I noticed something, why is your InitializeComponent() at the last line?  You should execute it first before the SetStyle, try the following:
public GradientPanel(
{
     InitializeComponent();
     this.SetStyle(ControlStyles.OptimizedDoubleBuffer | 
                        ControlStyles.UserPaint |
                        ControlStyles.AllPaintingInWmPaint, true);
     this.SetStyle(ControlStyles.ResizeRedraw, true);		
     this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
}

Open in new window

0
San24Author Commented:
Alfred1 - I changed it. Still get the flicker.

One thing I noticed was, when the form first starts out, there is absolutely no flicker. When I resize the form the flicker begins. Does that help? Anything to do with the Resize , which triggers the Paint which causes it?
0
Alfred A.Commented:
Setting this flag (ResizeRedraw) causes the control to repaint itself when resized.  Try removing it and check the result.
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
Can you show more code on how you draw the Gradient and explain the layout of your app?
0
San24Author Commented:
I commented out the over ride Create Params  and added the double buffer property in the Main Forms constructor itself. This seems to reduce the flicker when events are activated [Button click on the User Control] But I still have flicker when the user control loads or resized.

I`m uding the Hide() and Show() funtions in the User Control. Could this be causing it? Should I be using SuspendLayout() and ResumeLayout()?

Also, do I se Double buffer in each user control or just the main form?
public Form1()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);

         }

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

Open in new window

0
San24Author Commented:
Idle Mind - Here is the code I`m using for teh Gradient Panel from an online source.


public GradientPanel()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        }

		protected override void OnPaintBackground(PaintEventArgs e)
		{
            base.OnPaintBackground(e);

            int tmpShadowOffSet = Math.Min(Math.Min(_shadowOffSet, this.Width - 2), this.Height - 2);
            int tmpSoundCornerRadius = Math.Min(Math.Min(_roundCornerRadius, this.Width - 2), this.Height - 2);

            if (this.Width > 1 && this.Height > 1)
            {
                e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                Rectangle rect = new Rectangle(0, 0, this.Width - tmpShadowOffSet - 1, this.Height - tmpShadowOffSet - 1);
                Rectangle rectShadow = new Rectangle(tmpShadowOffSet, tmpShadowOffSet, this.Width - tmpShadowOffSet - 1, this.Height - tmpShadowOffSet - 1);

                GraphicsPath graphPathShadow = GradientPanelGraphics.GetRoundPath(rectShadow, tmpSoundCornerRadius);
                GraphicsPath graphPath = GradientPanelGraphics.GetRoundPath(rect, tmpSoundCornerRadius);

                if (tmpSoundCornerRadius > 0)
                {
                    using (PathGradientBrush gBrush = new PathGradientBrush(graphPathShadow))
                    {
                        gBrush.WrapMode = WrapMode.Clamp;
                        ColorBlend colorBlend = new ColorBlend(3);
                        colorBlend.Colors = new Color[]{Color.Transparent, 
                                                    Color.FromArgb(180, Color.DimGray), 
                                                    Color.FromArgb(180, Color.DimGray)};

                        colorBlend.Positions = new float[] { 0f, .1f, 1f };

                        gBrush.InterpolationColors = colorBlend;
                        e.Graphics.FillPath(gBrush, graphPathShadow);
                    }
                }

                // Draw backgroup
                LinearGradientBrush brush = new LinearGradientBrush(rect,
                    this._gradientStartColor,
                    this._gradientEndColor,
                    LinearGradientMode.BackwardDiagonal);
                e.Graphics.FillPath(brush, graphPath);
                e.Graphics.DrawPath(new Pen(Color.FromArgb(180, this._borderColor), _borderWidth), graphPath);

                // Draw Image
                if (_image != null)
                {
                    e.Graphics.DrawImageUnscaled(_image, _imageLocation);
                }
            }
		}

Open in new window

0
San24Author Commented:
Here is the Layout.

Main Form > Split Container > User Control > Tab Control > [User Control A, B and C]

Each of the A, B and C User Controls are contained in a Gradient panel.

The User Can switch between the A, B and C User Controls.I`m using Hide() and Show().
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
Well...one option to consider is to suppress repaints while the form is being resized and only redraw everything after the user lets go of the mouse.

See my comments in this thread:
http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_22087350.html

*You'd have to create and set a flag in your UserControls that gets toggled by the main form.
0
San24Author Commented:
Idle_Mind -  I toggle between the User Controls using  the Hide() and Show() Events - Do they call the repaint event? I can live with the slight amout of flickering during Resize [The user only resizes the app a few times] , but when I toggle between the User Controls the flickering is very noticeable.

In the simples form. This is what I have.
Button Click of User Control A I have -

  Hide();                                
  UserCntrlB.Show();

Button Click of User Control B I have -

  Hide();                                
  UserCntrlA.Show();
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
The parent control will refresh when the first control is hidden...and then will refresh again when the second control is shown.

You could try suppressing the parent control redraws until after both hide/shows have been done:
http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_21963638.html

You'd need to replace "this.Handle" in the link with the parent control (if it's not the form).
0
San24Author Commented:
Idle_Mind - So, if MainUserControl contains UserControls A, B and C. I have to use
the below code in the MainUserControl?


    private static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);
    private const int WM_SETREDRAW = 11;    
    SendMessage(this.Handle, WM_SETREDRAW, Convert.ToInt32(false), 0);  
    SendMessage(this.Handle, WM_SETREDRAW, Convert.ToInt32(true), 0);
0
San24Author Commented:
If it`s not too much to ask, a code snippet would really help. I`ve never used this?
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
"The User Can switch between the A, B and C User Controls.I`m using Hide() and Show()."

Where does the user indicate that they want to switch?  Does this occur in the main form itself?...or is it initiated from one of the UserControls?
0
San24Author Commented:
Its initiated from one of the User Controls.

Button Click of User Control A I have -

  Hide();                                
  UserCntrlB.Show();

Button Click of User Control B I have -

  Hide();                                
  UserCntrlA.Show();

Both User Control A and B or on a different UserControl - MainUserCntrol. MainUserControl is placed in a SplitContainer on the main form.
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
But it's the GradientPanel that is causing all the flicker no?

You'd either have to:
(1) Pass a reference to the Panel all the way down into the UserControl so it can directly manipulate it
(2) Make the UserControl raise a custom event that the Panel subscribes to so it knows when to toggle its painting
0
San24Author Commented:
Idle_Mind - Initially I thought it was the Gradient panel. I replaced the gradient panel with a regular panel. I still see the flicker. As you said its got to do with the Refresh, which calls the Paint method. How would you stop painting / refresh?
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
Try something like this...it might need some more tweaking though:
public const Int32 GA_ROOT = 2;
        [DllImport("user32.dll", ExactSpelling = true)]
        public static extern IntPtr GetAncestor(IntPtr hwnd, Int32 gaFlags);

        public const Int32 WM_SETREDRAW = 11;
        public static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);

        private void button1_Click(object sender, EventArgs e)        
        {
            // in UserCntrlA:
            IntPtr root = GetAncestor(this.Handle, GA_ROOT);
            SendMessage(root, WM_SETREDRAW, Convert.ToInt32(false), 0);

            this.Hide();
            UserCntrlB.Show();

            SendMessage(root, WM_SETREDRAW, Convert.ToInt32(true), 0);
        }

Open in new window

0
Mike TomlinsonMiddle School Assistant TeacherCommented:
*Not sure if turning off updates for the form will prevent the container with the usercontrols to also stop udpating.  Instead of getting the form, we may need to get the container of the UserControls.
0
San24Author Commented:
I can`t seem to add the user32.dll reference. I get an error "A reference tp 'C:\Windows\System32\user32.dll' could not be added. Please make sure the file is accessible, and that is a valid assembly or COM component"

Aaargh!
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
You need

    using System.Runtime.InteropServices;

And:

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);

Then follow it up with a Refresh() to the form:

            // in UserCntrlA:
            IntPtr root = GetAncestor(this.Handle, GA_ROOT);
            SendMessage(root, WM_SETREDRAW, Convert.ToInt32(false), 0);

            // ...toggle visibilities...

            SendMessage(root, WM_SETREDRAW, Convert.ToInt32(true), 0);
            Control.FromHandle(root).Refresh();
0
San24Author Commented:
I already have - using System.Runtime.InteropServices;
I tried to explicitly reference it from C:\Windows\System32 - Still get the error.

Warning      8      Method, operator, or accessor 'NewApproach.PatUserCntrl.SendMessage(System.IntPtr, int, int, int)' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation.      C:\CustomWinds\NewApproach\NewApproach\PatUserCntrl.cs      68      37      NewApproach


0
Mike TomlinsonMiddle School Assistant TeacherCommented:
"Consider adding a DllImport attribute to specify the external implementation."

The original post seemed to have lost that line.  My last post added it back in...

This:

    public static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);

Became:

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);
0
San24Author Commented:
@Idle Mind - Still have the flicker.
//THis is in AUserCntrl     

  [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetAncestor(IntPtr hwnd, Int32 gaFlags);

        public const Int32 WM_SETREDRAW = 11;
        public const Int32 GA_ROOT = 2;

        private void AUCntrlBut_Click(object sender, EventArgs e)
        {
                IntPtr root = GetAncestor(this.Handle, GA_ROOT);
                SendMessage(root, WM_SETREDRAW, Convert.ToInt32(false), 0);

                Hide();
                BCntrl.Show();

                SendMessage(root, WM_SETREDRAW, Convert.ToInt32(true), 0);
                Control.FromHandle(root).Refresh(); 
            }
        }

Open in new window

0
Mike TomlinsonMiddle School Assistant TeacherCommented:
=\

I'm pretty sure when you toggle visibility of something it causes a refresh message to start "bubbling up" the control hierarchy.  It may be that a different container needs to be suppressed instead of the form...you could try the Panel instead.

Any way you can upload some kind of test project that we can play with?
0
San24Author Commented:


I sure can, but it might take me some time to strip the code. Also, I`ll be off for the next two weeks starting tomorrow. I`ll do that as soon as I get back to the civilization. Thanks for your assistance, I`ll keep you updated. Hopefully the thread will not get closed for being inactive for two weeks.
0
San24Author Commented:
I changed the code so that Transparency property isn`t used, it seems to be better now..a lot better. Negligible flicker.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.