2010-06-24 131 views

回答

10

我明白了。发生这种情况是因为TabControl通过要求父控件在自己的窗口中绘制自己而部分地绘制自己。必要的,因为选项卡不覆盖控制的全部宽度,他们“伸出”。如果BackgroundImage绘制速度慢,则会在所绘制的背景与在其上绘制的选项卡之间看到闪烁。

这将很难解决,TabControl不支持任何类型的双缓冲。您只能通过使BackgroundImage高效绘制来最大限度地降低效果。您需要通过使图像与面板的ClientSize的大小完全一致来实现,以便图像不必调整大小。并用PixelFormat32bppPArgb像素格式创建该位图,通常比其他图像快10倍。

有一种魔法治疗可用,windows有一个样式标志,可以为整个窗口(包括其子控件)启用双缓冲。自XP以来支持,但一些副作用已被报告。这段代码粘贴到您的形式,它修复了TabControl的闪烁:

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

但要注意的是,视觉风格渲染的TabControl有这种风格的一个标志,而主要的不兼容。如果你的标签溢出,你得到的选择箭头,然后它去香蕉,并开始渲染标签一遍又一遍,产生非常高的闪烁率。

+0

这样做的窍门,谢谢! – jwarzech 2010-06-24 18:19:17

+0

忘记标记答案为答案,这解决了我的问题,谢谢:0) – jwarzech 2010-06-25 19:09:15

+0

真棒,拯救了我的一天! – BerggreenDK 2012-01-06 12:57:07

-1

您可以尝试创建一个使用双缓冲的面板。从面板派生并设置DoubleBuffered为true:

public partial class DoubleBufferedPanel : Panel 
    { 
     public DoubleBufferedPanel() 
     { 
     InitializeComponent(); 

     this.DoubleBuffered = true; 

     UpdateStyles(); 
     } 
    } 
0

我已经试过溶液的CreateParams,并介绍了自己的问题。我需要动态地添加和删除选项卡,并且在删除选项卡后,即使在Invalidate()方法之后,带WS_EX_COMPOSITED的TabControl也不会自我重绘。只有当我将鼠标移动到选项卡区域时,TabControl才会开始以非常奇怪的方式重绘自己。

所以最后我来到了这个解决方案:

public class TabControlX : TabControl 
{ 
    protected override void WndProc(ref Message m) 
    { 
     if(m.Msg == WinAPI.WM_MOUSEMOVE && !HotTrack) 
      return; 

     base.WndProc(ref m); 
    } 
} 

public const int WM_MOUSEMOVE = 0x0200; 

对于一些未知的原因HotTrack财产TabControl控件不工作,所以我其实已经固定它:)

当然,它在调整大小时不起作用,但它对我很好。

0

感谢来自Hans Passant的多个答案,我能够创建一个只有在需要时才会处于该模式的切换版本,在这种情况下:当选项卡控件由于父窗体而调整大小时。

这并不容易,因为我决定最好让它听取表单的大小调整开始并调整大小的结尾......这就是我所做的,它适合我的需求,tabcontrol不再闪烁从窗体调整大小正在调整大小。

public class NoFlickerTabControl : TabControl 
{ 
    #region PInvoke Change Window Rendering Style Params 

    public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, IntPtr dwNewLong) 
    { 
     if (IntPtr.Size == 8) 
     { 
      return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); 
     } 
     else 
     { 
      return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32())); 
     } 
    } 

    [DllImport("user32.dll", EntryPoint = "SetWindowLong")] 
    private static extern int SetWindowLong32(HandleRef hWnd, int nIndex, int dwNewLong); 

    [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")] 
    private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, IntPtr dwNewLong); 

    public enum WindowLongFlags : int 
    { 
     GWL_WNDPROC = -4, 
     GWL_HINSTANCE = -6, 
     GWL_HWNDPARENT = -8, 
     GWL_STYLE = -16, 
     GWL_EXSTYLE = -20, 
     GWL_USERDATA = -21, 
     GWL_ID = -12 
    } 

    #endregion 

    #region Tab Control Style! 

    public NoFlickerTabControl() 
    { 
     SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true); 
    } 

    #region Events to use from Parent 

    private bool bNeedToLinkFormResizeEvents = true; 

    private void ParentForm_ResizeBegin(object sender, EventArgs e) 
    { 
     EnableWS_EX_COMPOSITED(); 
    } 

    private void ParentForm_ResizeEnd(object sender, EventArgs e) 
    { 
     DisableWS_EX_COMPOSITED(); 
    } 

    #endregion 

    #region Enable/Disabled WS_EX_COMPOSITED 

    private const int WS_EX_COMPOSITED = 0x02000000; 

    private void EnableWS_EX_COMPOSITED() 
    { 
     CreateParams cp = CreateParams; 
     cp.ExStyle |= WS_EX_COMPOSITED; // Turn on WS_EX_COMPOSITED 
     //Make our call. 
     HandleRef handleRef = new HandleRef(null, Handle); 
     IntPtr style = new IntPtr(cp.ExStyle); 
     SetWindowLong(handleRef, (int)WindowLongFlags.GWL_EXSTYLE, style); 
    } 

    private void DisableWS_EX_COMPOSITED() 
    { 
     CreateParams cp = CreateParams; 
     cp.ExStyle &= ~WS_EX_COMPOSITED; // Turn OFF WS_EX_COMPOSITED (in case it's been set) 
     //Make our call. 
     HandleRef handleRef = new HandleRef(null, Handle); 
     IntPtr style = new IntPtr(cp.ExStyle); 
     SetWindowLong(handleRef, (int)WindowLongFlags.GWL_EXSTYLE, style); 
    } 

    #endregion 

    protected override void WndProc(ref Message m) 
    { 
     int WM_MOUSEMOVE = 0x0200; 
     if (m.Msg == WM_MOUSEMOVE && !HotTrack) 
     { 
      return; 
     } 

     base.WndProc(ref m); 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     if(Width <= 0 || Height <= 0) 
     { 
      return; 
     } 

     //Paint related, found it was best to do the check here when control finally gets Parent set by the program. 
     if (bNeedToLinkFormResizeEvents) 
     { 
      Form parentForm = FindForm(); 
      if (parentForm != null) 
      { 
       bNeedToLinkFormResizeEvents = false; 
       parentForm.ResizeBegin += ParentForm_ResizeBegin; 
       parentForm.ResizeEnd += ParentForm_ResizeEnd; 
      } 
     } 

     //~~~~~~ DO THE PAINTING OF THE CONTROL NOW. 

    } 

    #endregion 
}