为什么我的WinForms控制闪烁并缓慢resize?

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

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

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

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

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

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

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

你的主要表格

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

另外 ,您将控件的背景设置为“透明”,必须在WinForms中伪造,方法是让父窗口首先在控件窗口内绘制自己以生成背景像素。 然后控制器就自己开始了。 这比使用像SystemColors.Control这样的纯色填充控件的背景更有用,并且它会让你在combobox有机会自己绘制之前看到在调整表单大小时绘制的表单像素。

这是我从您的自定义渐变填充组框控件类中讨论的特定代码:

 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 +对象(画笔,笔,区域等),但没有设置它们中的任何一个! 几乎所有代码都应该包含在using语句中。 这只是草率的编码。

完成所有这些工作会花费一些成本。 当计算机被迫花费大量时间来渲染控件时,其他事情就会落后。 你看到一个闪烁,因为它紧张,以跟上resize。 它与计算机重载的任何其他东西没有什么不同(比如计算pi的值),当你使用像这里一样多的自定义绘制控件时,它真的很容易实现。 Win32中的透明度很难,很多自定义3D绘画也是如此。 它使UI看起来用户感觉笨拙。 另一个原因是我不理解本土控制的匆忙。

你真的只有三个选择:

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

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

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

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

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

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

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

也许一个很好的解决方案是使用Form.ResizeBegin和Form.ResizeEnd事件。

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

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

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

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

我有同样的问题。

因为你正在使用圆角 ,所以它会发生这种情况。 当我将CornerRadius属性设置为0时,闪烁消失了。

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

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