【问题标题】:TabControl blinks if image is background如果图像是背景,TabControl 会闪烁
【发布时间】:2010-06-24 16:21:43
【问题描述】:

我注意到,如果我在具有图像背景的面板中有一个 TabControl,则当鼠标悬停在选项卡上时,它会闪烁并重绘。是否有解决方法来防止这种情况发生?

【问题讨论】:

    标签: winforms tabcontrol


    【解决方案1】:

    我看到了。发生这种情况是因为 TabControl 部分通过要求父控件在其自己的窗口内绘制自身来绘制自身。这是必要的,因为选项卡没有覆盖控件的整个宽度,它们“伸出”。如果 BackgroundImage 的绘制速度很慢,您会看到正在绘制的背景和在其上绘制的选项卡之间闪烁。

    这将很难解决,TabControl 不支持任何类型的双缓冲。您只能通过使 BackgroundImage 有效地绘制来最小化效果。为此,您需要使图像的大小与面板的 ClientSize 完全相同,这样就不必调整图像的大小。并使用 PixelFormat32bppPArgb 像素格式创建该位图,它通常比其他格式快 10 倍。

    有一种灵丹妙药,窗口有一个样式标志,可以为整个窗口(包括其子控件)启用双缓冲。自 XP 以来受支持,但报告了一些副作用。将此代码粘贴到您的表单中,它修复了 TabControl 闪烁:

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

    但请注意,TabControl 的视觉样式渲染器与此样式标志有一个相当大的不兼容。如果您的选项卡溢出并且您看到选择箭头,那么它会变得很糟糕并开始一遍又一遍地渲染选项卡,从而产生非常高的闪烁率。

    【讨论】:

    • 忘记将回复标记为答案,这解决了我的问题,谢谢:0)
    【解决方案2】:

    我已尝试使用 CreateParams 解决方案,但它引入了自己的问题。我需要动态添加和删除选项卡,并且带有 WS_EX_COMPOSITED 的 TabControl 在我删除选项卡后不会重绘自身,即使在 Invalidate() 方法之后也是如此。只有当我将鼠标移动到选项卡区域时,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 控件中不起作用,所以我实际上已经修复了它:)

    当然,在调整大小时它不起作用,但对我来说没问题。

    【讨论】:

      【解决方案3】:

      多亏了包括 Hans Passant 在内的多个回答,我才能够制作一个切换版本,该版本仅在需要时处于该模式,在这种情况下:当标签控件因父表单而调整大小时。

      这并不容易,因为我决定最好让它听窗体 resize begin 和 resize end...这就是我所做的,它适合我的需要,tabcontrol 在从 Form 调整大小时不再闪烁调整大小。

      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
      }
      

      【讨论】:

        【解决方案4】:

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

           public partial class DoubleBufferedPanel : Panel
           {
              public DoubleBufferedPanel()
              {
                 InitializeComponent();
        
                 this.DoubleBuffered = true;
        
                 UpdateStyles();
              }
           }
        

        【讨论】:

          猜你喜欢
          • 2014-03-19
          • 1970-01-01
          • 1970-01-01
          • 2013-10-20
          • 2018-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多