Почему элементы управления WinForms мерцают и медленно меняют размеры? - PullRequest
11 голосов
/ 14 января 2011

Я создаю программу, в которой у меня много панелей и панелей в панелях.

У меня есть несколько пользовательских элементов управления, нарисованных на этих панелях.

Функция изменения размера 1 панелисодержит код для настройки размера и положения всех элементов управления на этой панели.

Теперь, как только я изменю размер программы, изменение размера этой панели становится активным.Это приводит к сильному миганию компонентов на этой панели.

Все элементы управления, нарисованные пользователем, имеют двойную буферизацию.

Может ли кто-нибудь помочь мне решить эту проблему?

Ответы [ 7 ]

18 голосов
/ 14 октября 2011

Чтобы избавиться от мерцания при изменении размера выигрышной формы, приостановите макет при изменении размера. Переопределите методы resizebegin / resizeend форм, как показано ниже.

protected override void OnResizeBegin(EventArgs e) {
    SuspendLayout();
    base.OnResizeBegin(e);
}
protected override void OnResizeEnd(EventArgs e) {
    ResumeLayout();
    base.OnResizeEnd(e);
}

Это оставит элементы управления нетронутыми (как они были до изменения размера) и приведет к перерисовке после завершения операции изменения размера.

17 голосов
/ 14 января 2011

Глядя на опубликованный вами проект, мерцание действительно плохо, когда у вас выбрана первая вкладка с градиентными рамками группы. При отображении второй или третьей вкладки мерцание практически отсутствует.

Your main form

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

Плюс , у вас задан фон элемента управления «Прозрачный», который необходимо подделать в WinForms, попросив родительское окно сначала нарисовать себя внутри окна элемента управления, чтобы получить фоновые пиксели. Контроль тогда рисует себя сверху этого. Это также более сложная задача, чем заливка фона элемента управления сплошным цветом, например SystemColors.Control, и это заставляет вас видеть нарисованные пиксели формы во время изменения размера формы, прежде чем групповые блоки получат возможность закрасить себя.

Вот конкретный код, о котором я говорю, из вашего пользовательского класса элемента управления groupbox, заполненного градиентом:

protected override void OnPaint(PaintEventArgs e)
{
    if (Visible)
    {
        Graphics gr = e.Graphics;
        Rectangle clipRectangle = new Rectangle(new Point(0, 0), this.Size);
        Size tSize = TextRenderer.MeasureText(Text, this.Font);
        Rectangle r1 = new Rectangle(0, (tSize.Height / 2), Width - 2, Height - tSize.Height / 2 - 2);
        Rectangle r2 = new Rectangle(0, 0, Width, Height);
        Rectangle textRect = new Rectangle(6, 0, tSize.Width, tSize.Height);

        GraphicsPath gp = new GraphicsPath();
        gp.AddRectangle(r2);
        gp.AddRectangle(r1);
        gp.FillMode = FillMode.Alternate;

        gr.FillRectangle(new SolidBrush(Parent.BackColor), clipRectangle);

        LinearGradientBrush gradBrush;
        gradBrush = new LinearGradientBrush(clipRectangle, SystemColors.GradientInactiveCaption, SystemColors.InactiveCaptionText, LinearGradientMode.BackwardDiagonal);
        gr.FillPath(gradBrush, RoundedRectangle.Create(r1, 7));

        Pen borderPen = new Pen(BorderColor);
        gr.DrawPath(borderPen, RoundedRectangle.Create(r1, 7));
        gr.FillRectangle(gradBrush, textRect);
        gr.DrawRectangle(borderPen, textRect);
        gr.DrawString(Text, base.Font, new SolidBrush(ForeColor), 6, 0);
    }

}

protected override void OnPaintBackground(PaintEventArgs pevent)
{
    if (this.BackColor == Color.Transparent)
        base.OnPaintBackground(pevent);
}

И теперь, когда вы увидели код, красные предупреждающие флажки должны подняться. Вы создаете связку объектов GDI + (кисти, ручки, области и т. Д.), Но не можете Dispose любой из них! Почти все этого кода следует обернуть в using операторов. Это просто небрежное кодирование.

Выполнение всей этой работы стоит чего-то. Когда компьютер вынужден тратить так много времени на рендеринг элементов управления, другие вещи отстают. Вы видите мерцание, поскольку оно напрягается, чтобы не отставать от изменения размера. Он ничем не отличается от всего, что перегружает компьютер (например, вычисление значения числа пи), это просто очень легко сделать, когда вы используете столько пользовательских элементов управления, как здесь. Прозрачность сложна в Win32, как и многие другие трехмерные изображения. Это делает пользовательский интерфейс и неуклюжим для пользователя. Еще одна причина, по которой я не понимаю спешку от нативного контроля.

У вас действительно есть только три варианта:

  1. Разобраться с мерцанием. (Я согласен, это не очень хороший вариант.)
  2. Используйте разные элементы управления, например стандартные, встроенные. Конечно, они могут не иметь эффект градиента, но в любом случае это будет выглядеть наполовину сломанным, если пользователь настроит свою тему Windows. Также довольно сложно читать черный текст на темно-сером фоне.
  3. Измените код рисования в ваших пользовательских элементах управления, чтобы выполнять меньше работы. Возможно, вам удастся справиться с некоторыми простыми «оптимизациями», которые не будут стоить вам никаких визуальных эффектов, но я подозреваю, что это маловероятно. Это компромисс между скоростью и эффектом глаз. Бездействие всегда быстрее.
2 голосов
/ 16 декабря 2011

Итак, я столкнулся с той же проблемой - мой элемент управления с прозрачным фоном перекрашивался примерно 34 раза, и для меня сработало:

В моей форме, содержащей элемент управления

protected override void OnResize(EventArgs e)
    {
        myControl.Visible = false;
        base.OnResize(e);
        myControl.Visible = true;
    }

И то же самое в элементе управления:

protected override void OnResize(EventArgs e)
    {
        this.Visible = false;
        base.OnResize(e);
        this.Visible = true;
    }

Это уменьшило количество перерисовок до 4, что эффективно устраняло любое мерцание при изменении размера элемента управления.

2 голосов
/ 25 июня 2011

Я успешно устраняю мерцание при изменении размера формы с помощью этого кода.Спасибо.

VB.NET

Public Class Form1

Public Sub New()
    Me.SetStyle(ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.SupportsTransparentBackColor, True)
End Sub

Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
    Me.Update()
End Sub

End Class

C #

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        Resize += Form1_Resize;
        this.SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.SupportsTransparentBackColor, true);
    }

    private void Form1_Resize(object sender, System.EventArgs e)
    {
        this.Update();
    }

}
1 голос
/ 11 мая 2011

Хотя подключение ResizeBegin и ResizeEnd - правильная идея, вместо того, чтобы скрывать видимость главной панели, я бы вместо этого отложил любые вычисления по изменению размера до ResizeEnd. В этом случае вам даже не нужно подключаться к ResizeBegin или Resize - вся логика переходит в ResizeEnd.

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

1 голос
/ 14 января 2011

Возможно, хорошим решением для вас будет использование Form.ResizeBegin и Form.ResizeEnd событий.

On ResizeBegin установить видимость главной панели на false, наResizeEnd установите для видимости главной панели значение true.

Таким образом, панели не будут перерисовываться, пока кто-то изменяет размер вашей формы.

0 голосов
/ 10 октября 2014

У меня была такая же проблема.

Похоже, что это происходит, потому что вы используете закругленные углы .Когда я установил для свойства CornerRadius значение 0, мерцание исчезло.

До сих пор я нашел только следующий обходной путь.Не самый хороший, но он останавливает мерцание.

private void Form_ResizeBegin(object sender, EventArgs e)
{
  rectangleShape.CornerRadius = 0;
}

private void Form_ResizeEnd(object sender, EventArgs e)
{
  rectangleShape.CornerRadius = 15;
}
...