2010-04-10 3 views
87

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

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

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

+0

Где эти заявления? В идеале поместите их в конструктор. Вы назвали «UpdateStyles» после их установки? Это плохо документировано, но иногда может быть необходимо. – Thomas

ответ

260

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

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

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

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

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

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

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

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

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

3

Если вы делаете какие-либо пользовательские картины в контроле (то есть наиважнейшая 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, вероятно, что вы хотите.

+0

Это творческий метод для имитации doublebuffer !. Вы можете добавить 'if (image! = Null) image.Dispose();' before 'image = new Bitmap ...' –

-2

Вы попробовали Control.DoubleBuffered Недвижимость?

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

Также this и this могут помочь.

-9

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

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

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

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

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

Итак, вы говорите, что ответ Ганса, предоставленный более двух лет назад, на самом деле , верный? Спасибо, Kshitiz. Это очень полезно! – Fernando

0

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

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

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

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

7

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

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

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

Итак, я искал способ отключить 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 event. 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; 
    } 
+0

Ваш код не включает метод TurnOnFormLevelDoubleBuffering() ... –

+0

@DanW Посмотрите URL, опубликованный в этом ответе (http://www.angryhacker.com/blog/archive/2010/07/21/how -в-получить избавиться-от-фликер-на-окон-форм-applications.aspx) – ChrisB

1

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

0

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

У меня было много проблем с Tabcontrol мерцание в форме с переопределением OnPaint и/или OnPaintBackGround в Windows 8 с использованием .NET 4.0.

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

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

0

я объединил this flicker fix и this font fix, то я должен был добавить немного мой собственный код, чтобы запустить таймер на краске Аннулируйте 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

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

1

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

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

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

и добавить в файл .cpp

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

Reflection делает трюк! Выбросьте это в свой конструктор или событие OnLoad, и если вы используете некоторые пользовательские UserControls, которые имеют вспомогательные элементы управления, вам необходимо убедиться, что эти пользовательские элементы управления с дополнительными элементами управления также имеют это.

ПРИМЕЧАНИЕ. Не используйте это в некоторых событиях, таких как OnPaint, которые выполняются триллионы раз в секунду, потому что отражение очень дорогое в использовании.

Пояснение: Родительский элемент управления/формы будет перебирать все элементы управления в форме и устанавливать их свойство в DoubleBuffered.

Код:

foreach (Control control in Controls) // reflection to sort flickering. 
    { 
     typeof(Control).InvokeMember("DoubleBuffered", 
      BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, 
      null, control, new object[] { true }); 
    } 
Смежные вопросы