Почему мои WinForms контролируют мерцание и изменение размера медленно?

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

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

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

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

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

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

Ответ 1

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

  Your main form

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

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

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

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, Это просто небрежное кодирование.

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

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

  • Смените мерцание. (Я согласен, это не очень хороший вариант.)
  • Используйте различные элементы управления, например стандартные, встроенные. Конечно, у них может не быть эффектного эффекта градиента, но это будет выглядеть сломанной половиной времени в любом случае, если пользователь настроил свою тему Windows. Также довольно сложно прочитать черный текст на темно-сером фоне.
  • Измените код рисования внутри пользовательских элементов управления, чтобы сделать меньше работы. Вы можете пройти с помощью простых "оптимизаций", которые не будут стоить вам каких-либо визуальных эффектов, но я подозреваю, что это маловероятно. Это компромисс между скоростью и глазными конфетами. Не делать ничего всегда быстрее.

Ответ 2

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

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

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

Ответ 3

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

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

С#

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();
    }

}

Ответ 4

Итак, я столкнулся с этой же проблемой - мой контроль с прозрачным фоном перерисовывался как 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, что эффективно устранило любое мерцание при изменении размера элемента управления.

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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

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

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

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

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