Как исправить мерцание в пользовательских элементах управления



в моем приложении я постоянно перемещается от одного элемента управления к другому. Я создал нет. пользовательских элементов управления, но во время навигации мои элементы управления мерцают. обновление занимает 1 или 2 секунды. Я пытался установить это



SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);


но это не помогло... Каждый элемент управления имеет одинаковое фоновое изображение с различными элементами управления.
Так что же это решение для него..

спасибо.

623   12  

12 ответов:

это не тот вид мерцания, который может решить двойная буферизация. Ни BeginUpdate, ни SuspendLayout. У вас слишком много элементов управления, фоновое изображение может сделать его много хуже.

Он начинается, когда UserControl рисует себя. Он рисует фоновое изображение, оставляя отверстия, куда идут окна дочернего элемента управления. Каждый дочерний элемент управления затем получает сообщение, чтобы нарисовать себя, они будут заполнять отверстие своим содержимым окна. Когда у вас есть много элементов управления, эти отверстия видны пользователю на некоторое время. Они обычно белые, плохо контрастирующие с фоновым изображением, когда оно темное. Или они могут быть черными, если форма имеет свой набор свойств непрозрачности или прозрачности, плохо контрастируя с чем угодно.

Это довольно фундаментальное ограничение Windows Forms, оно застряло с тем, как Windows отображает окна. Исправлено WPF кстати, он не использует windows для дочерних элементов управления. Что вы хотите-это двойная буферизация всей форме, включая дочерние элементы управления. Это возможно, проверьте мой код в этой теме для решения. Он имеет побочные эффекты, хотя, и на самом деле не увеличивает скорость окраски. Код прост, вставьте в форму (не пользовательский элемент управления):

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

есть много вещей, которые вы можете сделать, чтобы улучшить скорость живописи, к тому, что мерцание уже не заметно. Начните с решения фонового изображения. Они могут быть действительно дорого исходное изображение является большим и должно быть уменьшено, чтобы соответствовать элементу управления. Измените свойство BackgroundImageLayout на "плитка". Если это дает заметное ускорение, вернитесь к своей программе рисования и измените размер изображения, чтобы лучше соответствовать типичному размеру элемента управления. Или напишите код в методе OnResize() UC, чтобы создать копию изображения правильного размера, чтобы ее не нужно было изменять каждый раз, когда элемент управления перерисовывается. Используйте формат пикселей Format32bppPArgb для этой копии, он отображает около 10 раз быстрее, чем любой другой формат пикселей.

следующее, что вы можете сделать, это предотвратить отверстия от того, чтобы быть настолько заметным и контрастным плохо с изображением. Вы можете выключить флаг стиля WS_CLIPCHILDREN для UC, флаг, который предотвращает рисование UC в области, где идут дочерние элементы управления. Вставьте этот код в код UserControl:

protected override CreateParams CreateParams {
  get {
    var parms = base.CreateParams;
    parms.Style &= ~0x02000000;  // Turn off WS_CLIPCHILDREN
    return parms;
  }
}

дочерние элементы управления теперь будут рисовать себя поверх фонового изображения. Вы все еще можете увидеть они рисуют себя один за другим, но уродливая промежуточная белая или черная дыра не будет видна.

и последнее, но не менее важное: сокращение количества дочерних элементов управления всегда является хорошим подходом к решению проблем медленной живописи. Переопределите событие OnPaint() UC и нарисуйте то, что теперь показано в дочернем элементе. Конкретные метки и PictureBox являются очень расточительно. Удобно для точки и щелчка, но их легкая альтернатива (рисование строки или изображения) занимает только один строка кода в методе OnPaint ().

Это реальная проблема, и ответ, который дал Ханс Пассант, отлично подходит для сохранения мерцания. Тем не менее, есть побочные эффекты, как он упомянул, и они могут быть уродливыми (UI ugly). Как указано," Вы можете отключить флаг стиля WS_CLIPCHILDREN для UC", но это только отключает его для UC. Компоненты в основной форме по-прежнему имеют проблемы.

например, панель прокрутки не рисует, потому что она технически находится в дочерней области. Однако дочерний компонент не рисует полоса прокрутки, поэтому она не окрашивается до наведения мыши (или другого события, запускающего ее).

кроме того, анимированные значки (изменение значков в цикле ожидания) не работают. Удаление значков на вкладке.ImageKey не изменяет размер/перекрашивает другие вкладки соответствующим образом.

поэтому я искал способ отключить WS_CLIPCHILDREN на начальной живописи, так что моя форма будет загружаться красиво окрашены, или еще лучше только включить его при изменении размера моей формы с большим количеством компонентов.

в хитрость заключается в том, чтобы заставить приложение вызывать CreateParams с желаемым стилем WS_EX_COMPOSITED/WS_CLIPCHILDREN? Я нашел Хак здесь (http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) и это прекрасно работает. Спасибо AngryHacker!

я помещаю вызов TurnOnFormLevelDoubleBuffering () в событие ResizeBegin формы. TurnOffFormLevelDoubleBuffering () вызовите в виде события ResizeEnd (или просто оставьте его WS_CLIPCHILDREN после того, как он изначально окрашен правильно.)

    int originalExStyle = -1;
    bool enableFormLevelDoubleBuffering = true;

    protected override CreateParams CreateParams
    {
        get
        {
            if (originalExStyle == -1)
                originalExStyle = base.CreateParams.ExStyle;

            CreateParams cp = base.CreateParams;
            if (enableFormLevelDoubleBuffering)
                cp.ExStyle |= 0x02000000;   // WS_EX_COMPOSITED
            else
                cp.ExStyle = originalExStyle;

            return cp;
        }
    }

    public void TurnOffFormLevelDoubleBuffering()
    {
        enableFormLevelDoubleBuffering = false;
        this.MaximizeBox = true;
    }

Если вы делаете любую пользовательскую картину в элементе управления (т. е. переопределение OnPaint), вы можете попробовать двойную буферизацию самостоятельно.

Image image;
protected override OnPaint(...) {
    if (image == null || needRepaint) {
        image = new Bitmap(Width, Height);
        using (Graphics g = Graphics.FromImage(image)) {
            // do any painting in image instead of control
        }
        needRepaint = false;
    }
    e.Graphics.DrawImage(image, 0, 0);
}

и аннулировать свой контроль с помощью свойства NeedRepaint

в противном случае приведенный выше ответ с SuspendLayout и ResumeLayout, вероятно, то, что вы хотите.

в основной форме или пользовательском элементе управления, где находится фоновое изображение, установите BackgroundImageLayout свойство Center или Stretch. Вы заметите большую разницу, когда пользовательский элемент управления визуализацией.

Я попытался добавить это в качестве комментария, но у меня недостаточно очков. Это единственное, что когда-либо помогало моим мерцающим проблемам так много спасибо Хансу за его пост. Для тех, кто использует C++ builder, как я вот перевод

добавьте объявление CreateParams в основную форму вашего приложения .H-файл, например,

class TYourMainFrom : public TForm
{
protected:
    virtual void __fastcall CreateParams(TCreateParams &Params);
}

и добавить это к вашему .cpp file

void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
    Params.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    TForm::CreateParams(Params);
}

поместите код ниже в свой конструктор или событие OnLoad, и если вы используете какой-то пользовательский пользовательский элемент управления, имеющий дополнительные элементы управления, вам нужно будет убедиться, что эти пользовательские элементы управления также дважды буферизованы (хотя в документации MS они говорят, что по умолчанию установлено значение true).

если вы создаете пользовательский элемент управления, вы можете добавить этот флаг в свой ctor:

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

дополнительно вы можете использовать этот код в ваш Форма / Управление:

foreach (Control control in Controls)
{
    typeof(Control).InvokeMember("DoubleBuffered",
        BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
        null, control, new object[] { true });
}

мы перебираем все элементы управления в форме / control и получаем доступ к их DoubleBuffered свойство, а затем мы меняем его на true для того, чтобы сделать каждый элемент управления на форме двойной буферизации. Причина, по которой мы здесь размышляем, заключается в том, что представьте, что у вас есть элемент управления, который имеет дочерние элементы управления, которые недоступны, таким образом, даже если они являются частными элементами управления, мы все равно изменим их свойство на true.

дополнительная информация о двойной буферизации технику можно найти здесь.

есть еще одно свойство, которое я обычно переопределяю для сортировки этой проблемы:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams parms = base.CreateParams;
        parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
        return parms;
    }
}

WS_EX_COMPOSITED - рисует все потомки окна в порядке рисования снизу вверх с использованием двойной буферизации.

вы можете найти больше из этих флагов стиле здесь.

надеюсь, что это поможет!

просто чтобы добавить к ответу Ганс дал:

(версия TLDR: прозрачность тяжелее, чем вы думаете, используйте только сплошные цвета везде)

Если WS_EX_COMPOSITED, DoubleBuffered и WS_CLIPCHILDREN не решили ваше мерцание (для меня WS_CLIPCHILDREN сделал это еще хуже), попробуйте следующее: пройдите через все ваши элементы управления и весь ваш код, и везде, где у вас есть какая-либо прозрачность или полупрозрачность для BackColor, ForeColor или любого другого цвета, просто удалите его, используйте только solid цвета. В большинстве случаев, когда вы думаете, что вы просто есть использовать прозрачность, вы не. Ре-дизайн вашего кода и элементов управления, и использовать сплошные цвета. У меня было ужасное, ужасное мерцание, и программа работала вяло. Как только я удалил прозрачность, она значительно ускорилась, и есть 0 мерцание.

EDIT: чтобы добавить дальше, я только что обнаружил, что WS_EX_COMPOSITED не должен быть оконным, он может быть применен только к определенным элементам управления! Это спасло меня от многого беда. Просто создайте пользовательский элемент управления, унаследованный от любого элемента управления, который вам нужен, и вставьте уже опубликованное переопределение для WS_EX_COMPOSITED. Таким образом, вы получаете низкоуровневый двойной буфер только на этом элементе управления, избегая неприятных побочных эффектов в остальной части приложения!

Я знаю, что этот вопрос очень старый, но хочу дать свой опыт по нему.

у меня было много проблем с Tabcontrol мерцание в форме с перекрытая OnPaint и/или OnPaintBackGround в Windows 8 с помощью .NET 4.0.

единственная мысль, которая работала, была НЕ ИСПОЛЬЗОВАТЬ the Graphics.DrawImage метод OnPaint переопределяет, другими словами, когда draw был сделан непосредственно на графику, предоставленную PaintEventArgs, даже рисуя весь прямоугольник, мерцание исчезло. Но если позвонить то DrawImage метод, даже рисуя обрезанное растровое изображение (созданное для двойной буферизации), появляется мерцание.

надеюсь, что это помогает!

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

все три делают это:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TabControlEx:TabControl
{
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    private const int WM_PAINT = 0x0f;
    private const int WM_SETFONT = 0x30;
    private const int WM_FONTCHANGE = 0x1d;
    private System.Drawing.Bitmap buffer;
    private Timer timer = new Timer();
    public TabControlEx()
    {
        timer.Interval = 1;
        timer.Tick += timer_Tick;
        this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }
    void timer_Tick(object sender, EventArgs e)
    {
        this.Invalidate();
        this.Update();
        timer.Stop();
    }
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_PAINT) timer.Start();
        base.WndProc(ref m);
    }
    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);
    }
    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        this.OnFontChanged(EventArgs.Empty);
    }
    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);
        IntPtr hFont = this.Font.ToHfont();
        SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
        SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
        this.UpdateStyles();
    }
}

Я не создатель, но из того, что я понимаю, растровое изображение делает все ошибки в обход.

Это было единственное, что окончательно решило TabControl (со значками) мерцание для мне.

разница видео результата: ваниль элемент управления TabControl против tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

ps. вам нужно будет установить HotTrack = true, потому что это исправляет эту ошибку тоже

ты Control.DoubleBuffered собственность?

возвращает или задает значение, указывающее, должен ли этот элемент управления перерисовывать свою поверхность с помощью вторичного буфера для уменьшения или предотвращения мерцания.

и этой и этой может помочь.

нет необходимости в какой-либо двойной буферизации и все такое прочее, ребята...

простое решение...

Если вы используете интерфейс MDI, просто вставьте код ниже в основной форме. Он удалит все мерцание со страниц. Однако некоторые страницы, которые требуют больше времени для загрузки showup в 1 или 2 секунды. Но это лучше, чем показывать мерцающую страницу, на которой каждый элемент приходит один за другим.

это единственное лучшее решение для все приложение. Смотрите код, чтобы положить в основной форме:

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

Comments

    Ничего не найдено.