TabControl мигает, если изображение фоновое - PullRequest
3 голосов
/ 24 июня 2010

Я заметил, что если у меня есть TabControl на панели с фоновым изображением, когда курсор мыши находится над вкладкой, он мигает и перерисовывается. Есть ли способ избежать этого?

Ответы [ 4 ]

10 голосов
/ 24 июня 2010

Я вижу это.Это происходит потому, что 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 голосов
/ 17 августа 2017

Благодаря нескольким ответам, в том числе от Ханса Пассанта, я смог создать переключенную версию, которая будет в этом режиме только тогда, когда это необходимо, в данном случае: когда размер элемента управления вкладками изменялся из-за родительской формы.

Это было нелегко, так как я решил, что лучше всего, чтобы он слушал начало изменения размера формы и конец изменения размера ... Вот что я сделал, и это соответствует моим потребностям, больше не мерцает 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
}
0 голосов
/ 18 сентября 2013

Я пробовал решение с CreateParams , и оно представляет свои проблемы. Мне нужно динамически добавлять и удалять вкладки, и TabControl с WS_EX_COMPOSITED не перерисовывает себя после удаления вкладки, даже после метода 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, поэтому я на самом деле исправил его:)

Конечно, это не работает во время изменения размера, но у меня все в порядке.

0 голосов
/ 24 июня 2010

Вы можете попробовать создать панель, которая использует двойную буферизацию. Получите из панели и установите DoubleBuffered в true:

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

         this.DoubleBuffered = true;

         UpdateStyles();
      }
   }
...