为什么我的WinForms控制闪烁并缓慢调整大小?

时间:2011-01-14 11:12:24

标签: c# winforms controls resize flicker

我正在制作一个程序,我在面板中有很多面板和面板。

我在这些面板中有一些自定义绘制的控件。

1面板的调整大小功能包含调整该面板中所有控件的大小和位置的代码。

现在,只要我调整程序大小,就会激活此面板的调整大小。这会导致此面板中的组件出现大量闪烁。

所有用户绘制的控件都是双缓冲的。

有人可以帮我解决这个问题吗?

8 个答案:

答案 0 :(得分:18)

要在调整win表单大小时消除闪烁,请在调整大小时暂停布局。覆盖表单resizebegin / resizeend方法如下。

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

这将使控件保持原样(在调整大小之前),并在调整大小操作完成时强制重绘。

答案 1 :(得分:17)

查看您发布的项目,当您选择第一个选项卡时,使用渐变填充的组框时,闪烁非常糟糕。显示第二个或第三个标签时,几乎没有任何闪烁,如果有的话。

Your main form

很明显,这个问题与您在该标签页上显示的控件有关。快速浏览自定义渐变填充组框类的代码可以提供更具体的原因。每次绘制其中一个groupbox控件时,你正在进行非常昂贵的处理 lot 因为每个组框控件必须在每次调整表单大小时自行重绘,所以该代码的执行次数难以置信。

Plus ,你将控件的背景设置为“透明”,必须在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绘画也是如此。它使UI看起来对用户感到笨拙。另一个原因是我不理解本土控制的匆忙。

你真的只有三个选择:

  1. 处理闪烁。 (我同意,这不是一个好的选择。)
  2. 使用不同的控件,例如标准的内置控件。当然,他们可能没有花哨的渐变效果,但如果用户已经定制了他们的Windows主题,那么无论如何,这种情况看起来会被打破一半。在深灰色背景上阅读黑色文字也相当困难。
  3. 更改自定义控件中的绘制代码以减少工作量。您可以通过一些简单的“优化”来获得,这些优化不会让您失去任何视觉效果,但我怀疑这不太可能。这是速度和眼睛糖果之间的权衡。什么都不做总是更快。

答案 2 :(得分:2)

使用此代码调整表单大小时,我成功消除了闪烁。感谢。

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

}

答案 3 :(得分:2)

所以我遇到了同样的问题 - 我的透明背景控制重新涂抹了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,这有效地消除了控件调整大小时的任何闪烁。

答案 4 :(得分:1)

对您来说,一个好的解决方案可能就是使用Form.ResizeBeginForm.ResizeEnd个事件。

在ResizeBegin上将主面板可见性设置为false,在ResizeEnd上将主面板可见性设置为true。

这样,当有人调整表单大小时,不会重新绘制面板。

答案 5 :(得分:1)

虽然挂钩ResizeBegin和ResizeEnd是正确的想法,而不是隐藏主面板的可见性,而是延迟任何调整大小计算,直到ResizeEnd。在这种情况下,您甚至不需要挂钩ResizeBegin或Resize - 所有逻辑都进入ResizeEnd。

我说这有两个原因。其一,即使面板被隐藏,调整大小操作可能仍然很昂贵,因此除非调整大小计算被延迟,否则表单将不会像它应该的那样响应。二,在调整大小时隐藏窗格的内容可能会让用户感到不安。

答案 6 :(得分:0)

我遇到了同样的问题。

由于您正在使用圆角,因此发生了这种情况。当我将CornerRadius属性设置为0时,闪烁消失了。

到目前为止,我只找到了以下解决方法。不是最好的,但它会阻止闪烁。

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

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

答案 7 :(得分:0)

我遇到过类似的问题。我的整个表格正在缓慢调整大小,控件以丑陋的方式绘制它们。所以这对我有帮助:

//I Added This To The Designer File, You Can Still Change The WindowState In Designer View If You Want. This Helped Me Though.
this.WindowState = FormWindowState.Maximized;

并在 Resize 事件中,将此代码添加到开头

this.Refresh();