Link to home
Start Free TrialLog in
Avatar of San24
San24

asked on

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

ASKER CERTIFIED SOLUTION
Avatar of Alfred A.
Alfred A.
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of San24
San24

ASKER

Alfred1 - I already tried that. Doesn`t seem to make any difference. The flicker is still present.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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

Avatar of San24

ASKER

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?
Setting this flag (ResizeRedraw) causes the control to repaint itself when resized.  Try removing it and check the result.
Avatar of Mike Tomlinson
Can you show more code on how you draw the Gradient and explain the layout of your app?
Avatar of San24

ASKER

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

Avatar of San24

ASKER

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

Avatar of San24

ASKER

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().
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of San24

ASKER

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();
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:
https://www.experts-exchange.com/questions/21963638/Prevent-control-flashing-on-multiple-changes.html

You'd need to replace "this.Handle" in the link with the parent control (if it's not the form).
Avatar of San24

ASKER

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);
Avatar of San24

ASKER

If it`s not too much to ask, a code snippet would really help. I`ve never used this?
"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?
Avatar of San24

ASKER

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.
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
Avatar of San24

ASKER

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?
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

*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.
Avatar of San24

ASKER

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!
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();
Avatar of San24

ASKER

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


"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);
Avatar of San24

ASKER

@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

=\

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?
Avatar of San24

ASKER



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.
Avatar of San24

ASKER

I changed the code so that Transparency property isn`t used, it seems to be better now..a lot better. Negligible flicker.