Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Winforms TabControl在图像为背景时闪烁_Winforms_Tabcontrol - Fatal编程技术网

Winforms TabControl在图像为背景时闪烁

Winforms TabControl在图像为背景时闪烁,winforms,tabcontrol,Winforms,Tabcontrol,我注意到,如果在具有图像背景的面板中有一个TabControl,当鼠标悬停在选项卡上时,它会闪烁并重新绘制。是否有一个解决方法来防止这种情况发生?我明白了。之所以会发生这种情况,是因为TabControl部分通过要求父控件在其自己的窗口内绘制自身来绘制自身。必要的,因为标签没有覆盖控件的整个宽度,所以它们“突出”。如果背景图像绘制速度慢,您将看到正在绘制的背景与在其上绘制的选项卡之间闪烁 这将很难解决,TabControl不支持任何类型的双缓冲。您只能通过使背景图像高效地绘制来最小化效果。要做到

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

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

这将很难解决,TabControl不支持任何类型的双缓冲。您只能通过使背景图像高效地绘制来最小化效果。要做到这一点,您需要使图像的大小与面板的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的视觉样式呈现程序与此样式标志有一个相当大的不兼容性。如果您的选项卡溢出,并且您得到了选择箭头,则它会变得异常,并开始一次又一次地渲染选项卡,从而产生非常高的闪烁率。

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

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

         this.DoubleBuffered = true;

         UpdateStyles();
      }
   }

我用CreateParams尝试了解决方案,它引入了自己的问题。我需要动态添加和删除选项卡,并且在我删除选项卡之后,即使在使用Invalidate()方法之后,使用WS_EX_COMPOSITED的TabControl也不会重新绘制自身。只有当我将鼠标移动到tabs区域时,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;
由于一些未知的原因,热跟踪属性在TabControl中不起作用,所以我实际上已经修复了它:)


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

多亏了包括Hans Passant在内的多个答案,我能够制作一个切换版本,只有在需要时才处于该模式,在这种情况下:当tab控件因父窗体而调整大小时

这并不容易,因为我决定最好让它听一下表单resizebegin和resizeend。。。这就是我所做的,它适合我的需要,不再从正在调整大小的窗体调整大小时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
}
public类noflicketabcontrol:TabControl
{
#区域PInvoke更改窗口渲染样式参数
公共静态IntPtr SetWindowLong(HandleRef hWnd、int nIndex、IntPtr dwNewLong)
{
如果(IntPtr.Size==8)
{
返回SetWindowLongPtr64(hWnd、nIndex、dwNewLong);
}
其他的
{
返回新的IntPtr(SetWindowLong32(hWnd,nIndex,dwNewLong.ToInt32());
}
}
[DllImport(“user32.dll”,EntryPoint=“SetWindowLong”)]
私有静态外部int SetWindowLong32(HandleRef hWnd、int nIndex、int dwNewLong);
[DllImport(“user32.dll”,EntryPoint=“SetWindowLongPtr”)]
私有静态外部IntPtr SetWindowLongPtr64(HandleRef hWnd、int nIndex、IntPtr dwNewLong);
公共枚举WindowLongFlags:int
{
GWL_WNDPROC=-4,
GWL_HINSTANCE=-6,
GWL_HWNDPARENT=-8,
GWL_风格=-16,
GWL_EXSTYLE=-20,
GWL_USERDATA=-21,
GWL_ID=-12
}
#端区
#区域选项卡控件样式!
公共控制
{
SetStyle(ControlStyles.AllPaintingWimPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDubleBuffer,true);
}
#要从父级使用的区域事件
private bool bNeedToLinkFormResizeEvents=true;
私有void ParentForm_ResizeBegin(对象发送方,事件参数e)
{
EnableWS_EX_COMPOSITED();
}
私有void ParentForm_ResizeEnd(对象发送方,事件参数e)
{
DisableWS_EX_COMPOSITED();
}
#端区
#区域启用/禁用WS_EX_合成
私有常量int WS_EX_COMPOSITED=0x020000000;
私有void启用项\u EX\u COMPOSITED()
{
CreateParams cp=CreateParams;
cp.ExStyle |=WS_EX_COMPOSITED;//启用WS_EX_COMPOSITED
//打电话。
HandleRef HandleRef=新HandleRef(null,Handle);
IntPtr样式=新的IntPtr(cp.ExStyle);
SetWindowLong(handleRef,(int)WindowLongFlags.GWL_EXSTYLE,style);
}
私有void DisableWS_EX_COMPOSITED()
{
CreateParams cp=CreateParams;
cp.ExStyle&=~WS\u EX\u COMPOSITED;//关闭WS\u EX\u COMPOSITED(如果已设置)
//打电话。
HandleRef HandleRef=新HandleRef(null,Handle);
IntPtr样式=新的IntPtr(cp.ExStyle);
SetWindowLong(handleRef,(int)WindowLongFlags.GWL_EXSTYLE,style);
}
#端区
受保护的覆盖无效WndProc(参考消息m)
{
int WM_MOUSEMOVE=0x0200;
if(m.Msg==WM_MOUSEMOVE&&!热跟踪)
{
返回;
}
基准WndProc(参考m);
}
受保护的覆盖无效OnPaint(PaintEventArgs e)
{

如果(Width忘记将响应标记为答案,这解决了我的问题,谢谢:0)