dcsimg
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 8 of 8

Thread: Flicker In TabControl

  1. #1
    Join Date
    Dec 2003
    Posts
    3

    Flicker In TabControl

    Okay, so I want to use the native .Net TabControl in one of my apps, but it flickers like crazy during resize. So I've been attempting to find a resolution.

    I found a lot of chatter on the newsgroups about using double-bufferring by deriving a class from TabControl and then using this code:
    Code:
    this.SetStyle(ControlStyles.UserPaint, true);
    this.SetStyle(ControlStyles.DoubleBuffer, true);
    this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    Well, that didn't work. The control wouldn't even paint to the screen, even after I overrode OnPaint and passed it back to the base. I saw some postings that said I couldn't override OnPaint when using ControlStyles like DoubleBuffer, but I didn't investigate further.

    I then read an article about getting rid of flicker in the ListView control by overriding WndProc. So I tried this code:
    Code:
    protected override void WndProc(ref Message messg)
    {
    if ((int)WM.WM_ERASEBKGND == messg.Msg)
       messg.Msg = (int) WM.WM_NULL;
    base.WndProc(ref messg);			
    }
    That got rid of the flicker, but I get garbage around the border of the control. I would be very grateful if anyone could provide some assistance on this matter.

    Thanks!

    Colin

  2. #2
    Join Date
    Nov 2003
    Posts
    76

    Question

    Have you tried using the methods, SuspendLayout and ResumeLayout?

  3. #3
    Join Date
    Dec 2003
    Posts
    3
    Calling SuspendLayout right before I do a resize does get rid of the flicker, but the problem with that approach is that none of the controls on the form will resize themselves until ResumeLayout is called.

    To explain further, let me describe the sequence of events:

    1. User holds down mouse (to resize form), SuspendLayout is called.

    2. User resizes form

    3. User releases mouse button, ResumeLayout is called.

    4. All the controls will now appear resized but in the time between steps 1 and 4 the controls appear - to the user - to be not responding to the resize.

    The resulting effect doesn't look very professional, in my opinion. Is there some other approach using Suspend/Resume-Layout to get rid of flicker, but that also gives the appearance that the controls are dynamically resizing, that you had in mind?

    Thanks!

  4. #4
    Join Date
    Dec 2003
    Posts
    3
    After working on this a little more I still don't have a solution, but I think I have more information I can at least add to my question.

    To double-buffer the tabcontrol one has to use SetStyle in the constructor for a class that inherits from System.Windows.Forms.TabControl
    Code:
    SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
    Note that UserPaint must be switched on in order for DoubleBuffer to be enabled. When I used this method, however, the tabcontrol was not painted to the screen. I just got grey where the tabs and their borders should be. I can still click on each tab (though they are not visible) and load that tab's tabpage, but the effect is obviously not what we want.

    My first line of thinking was, "Well, since I have set UserPaint to true that means I am now responsible for all painting of this control, so I have to handle OnPaint." I think that logic is basically true. I found a transcript of an MSDN chat that seemed to confirm this:

    http://msdn.microsoft.com/chats/vstu...dio_070202.asp

    Seeing that all I wanted to do was enable double-bufferring of the tabcontrol - and not rewrite it myself - I thought all I should need to do in OnPaint would be to pass the call back to the base:
    Code:
    protected override void OnPaint(PaintEventArgs e)	
    {
    base.OnPaint(e);
    }
    However, this does not do anything. The derived control's tabs still do not paint.

    So at this point I am thinking that to get a double-buffered tab control I am going to have to write the whole control myself! I took a look at the source code for the Magic Library's tab control (http://www.dotnetmagic.com, http://sourceforge.net/projects/dotnetmagic). In the source code for the tab control he derives his control from the Panel class, and not the TabControl.

    Okay, so now I think I should just use this MagicLibrary control to accomplish what I want (a non-flickering tab control!) and move on. But, I am a stubborn a** so I want to see if I can still solve my problem using just the canned .Net control.

    Since I can't get double buffering to work without re-inventing the wheel I am back to looking at my handling of WM_ERASEBKGND in the control's WndProc. I still have that garbage problem around the border of the control. I've noticed that the garbage around the border is really "bleed through" from the windows behind the form the control is contained in. In other words, when I ignore WM_ERASEBKGND the borders of the tab control become transparent.

    If anyone has any further advice or education they can provide me on this topic I would be very appreciative. For right now, however, I would probably recommend that any one looking to stop flicker in the TabControl should just use another control instead.

    Thanks!

  5. #5
    Join Date
    Mar 2007
    Posts
    59

    Re: Flicker In TabControl

    Hello,

    That's a really old thread, but I'm having the same problem and I already loosed more than an hour on that. I've try many things but without success. I don't know if somebody managed to solve that without using another library or rewriting the tabcontrol, but here is my temporary working solution for now:

    1 - put the webcontrol (or other control that cause flickering) somewhere in your form (but not docked and child of the tabpanel).
    2 - create functions for the OnResize and OnTabChanged event of the tab control
    * In the OnResize, set the width and height of your webcontrol according to the new values of the tabcontrol. You might need to tweak some values since the tabcontrol size is bigger than the desired size.
    * In the OnTabChanged, show/hide when the proper tab is displayed.

    After that, the resize works like a charm. That's probably one of the biggest patch I've made so far with C# in order to have a decent UI. I don't know why this control is buggy, but I think MS should really fix that.

  6. #6
    Join Date
    May 2004
    Posts
    31

    Lightbulb Re: Flicker In TabControl

    As stated in the previous post, this is an old thread, but my solution is new :-). Took a couple hours of poking, but here it is:

    For the TabControl replacement:
    Code:
    using System;
    
    namespace System.Windows.Forms
    {
        class DoubleBufferedTabControl : System.Windows.Forms.TabControl
        {
            public DoubleBufferedTabControl()
            {
                this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
            }
    
            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);
            }
            private System.Drawing.Bitmap buffer;
        }
    }
    For the TabPage you will also need:
    Code:
    using System;
    
    namespace System.Windows.Forms
    {
        class DoubleBufferedTabPage : System.Windows.Forms.TabPage
        {
            public DoubleBufferedTabPage()
            {
                this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |ControlStyles.DoubleBuffer, true);
            }
        }
    }
    The UserPaint style must be on for OnPaint to be called. In the middle of OnPaint, the UserPaint style is removed allowing it to be drawn as normal. To actually get this drawing, though, we need to force another immediate paint. To do this it is rendered to a bitmap and then drawn on the screen. To finish, UserPaint is set again. This method brought flicker down to a negligible level for me. The only disadvantage I've found is that it is slower, especially when no theming is applied (old Win95 look). The easiest way to use it, is to design your tabs using the regular classes and then use a Search/Replace to change the class names.

    I dunno if .Net 3.0 fixes this, but hope this helps somebody!
    Last edited by coder0xff; June 22nd, 2007 at 12:11 AM. Reason: Uneeded code removed
    Brent
    CoderSoft

  7. #7
    Join Date
    Apr 2011
    Posts
    1

    Thumbs up Re: Flicker In TabControl

    Again, even older still but I ran into this with VS2008

    That code fixed it! I'm now going to do the same for my slider control instead of hiding it and showing it again when the resize stops.

    I translated it into VB.NET in case anyone needs it.

    I also finally caught on to the fact that the control needed to be redefined in the form.designer.vb file as the new DoubleBuffedTabControl and DoubleBufferedTabPage instead of the default system.windows.forms.tabcontrol.

    Code:
        Class DoubleBufferedTabControl
            Inherits System.Windows.Forms.TabControl
            Public Sub New()
                Me.SetStyle(ControlStyles.UserPaint Or ControlStyles.DoubleBuffer Or ControlStyles.AllPaintingInWmPaint, True)
            End Sub
    
            Protected Overrides Sub OnPaint(ByVal pevent As PaintEventArgs)
                Me.SetStyle(ControlStyles.UserPaint, False)
                MyBase.OnPaint(pevent)
                Dim o As System.Drawing.Rectangle = pevent.ClipRectangle
                System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control)
                If o.Width > 0 AndAlso o.Height > 0 Then
                    DrawToBitmap(buffer, New System.Drawing.Rectangle(0, 0, Width, o.Height))
                End If
                pevent.Graphics.DrawImageUnscaled(buffer, 0, 0)
                Me.SetStyle(ControlStyles.UserPaint, True)
            End Sub
    
            Protected Overrides Sub OnResize(ByVal e As EventArgs)
                MyBase.OnResize(e)
                buffer = New System.Drawing.Bitmap(Width, Height)
            End Sub
            Private buffer As System.Drawing.Bitmap
        End Class
        Class DoubleBufferedTabPage
            Inherits System.Windows.Forms.TabPage
            Public Sub New()
                Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.DoubleBuffer, True)
            End Sub
        End Class
    Last edited by HanneSThEGreaT; April 29th, 2011 at 01:15 AM.

  8. #8
    Join Date
    Jul 2001
    Location
    Sunny South Africa
    Posts
    11,267

    Re: Flicker In TabControl

    This is in C# :

    Code:
    using Microsoft.VisualBasic;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    class DoubleBufferedTabControl : System.Windows.Forms.TabControl
    {
    	public DoubleBufferedTabControl()
    	{
    		this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    	}
    
    	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);
    	}
    	private System.Drawing.Bitmap buffer;
    }
    class DoubleBufferedTabPage : System.Windows.Forms.TabPage
    {
    	public DoubleBufferedTabPage()
    	{
    		this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
    	}
    }
    Not sure if it works, will see later on
    [SIGPIC][/SIGPIC]
    All my Articles
    Hannes

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width




On-Demand Webinars (sponsored)