Winforms:SuspendLayout/ResumeLayout是不够的?

Mar*_*ini 26 c# performance gdi+ doublebuffered winforms

我有一些库"自定义控件".基本上我们有自己的按钮,圆角板和一些带有一些定制油漆的组合箱.尽管OnPaint方法中存在"数学",但控件非常标准.大多数情况下,我们所做的只是绘制圆角并为背景添加渐变.我们使用GDI +来实现这一切.

这些控件都可以(根据我们的客户非常好看),但是尽管有DoubleBuffer,你可以看到一些重绘,特别是当同一个表格上有20个++按钮时(例如).在表单加载时,您会看到绘制的按钮...这很烦人.

我很确定我们的按钮不是地球上最快的东西,但我的问题是:如果双缓冲区"打开",那么不应该在后台进行所有重绘,而Windows子系统应该"立即"显示结果吗?

另一方面,如果存在将创建标签的"复杂"foreach循环,则将它们添加到面板(双缓冲)并更改其属性,如果我们在循环之前挂起面板并在循环时恢复面板布局结束,不应该所有这些控件(标签和按钮)"几乎立即"出现?这不会发生这种情况,您可以看到面板被填充.

知道为什么不发生这种情况吗?我知道很难在没有示例代码的情况下进行评估,但这也难以复制.我可以用相机制作一个视频,但相信我这个,它不是很快:)

Jud*_*ngo 12

我们也看到了这个问题.

我们已经看到"修复"它的一种方法是完全暂停控制的绘制,直到我们准备好去.为此,我们将WM_SETREDRAW消息发送到控件:

// Note that WM_SetRedraw = 0XB

// Suspend drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);

...

// Resume drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
Run Code Online (Sandbox Code Playgroud)

  • 它不是C++,它是命名PInvoke类的一种非常标准的方式.您应该创建自己的静态类并添加名为SendMessage的PInvoke方法.WindowMessages显然也是一个用户定义的枚举,其值从Win32 API中正确指定.最后,亚当,你没有发布这个,你刚刚发布了一个链接,你的态度非常烦人. (16认同)

小智 11

您应该注意的一件事是您是否在面板的任何子控件上设置了BackColor = Transparent.BackColor = Transparent将显着降低渲染性能,尤其是在父面板使用渐变时.

Windows窗体不使用真正的透明度,而是使用"假的".每个子控件绘制调用都会在父级上生成绘制调用,因此父级可以绘制其子控件绘制其内容的背景,使其显示为透明.

因此,如果您有50个子控件,将在父控件上生成额外的50个绘制调用以进行背景绘制.由于渐变通常较慢,因此性能会下降.

希望这可以帮助.


Don*_*ter 9

我会从性能角度来解决你的问题.

foreach循环将创建标签,将它们添加到面板(双缓冲)并更改其属性

如果这是订单的完成,那么还有改进的余地.首先创建所有标签,更改其属性,并在它们准备就绪后,将它们添加到面板:Panel.Controls.AddRange(Control[])

大多数情况下,我们所做的只是绘制圆角并为背景添加渐变

你一遍又一遍地做同样的事吗?你的渐变是如何产生的?写一个图像不会那么慢.我曾经不得不在内存中创建一个1680x1050的渐变,而且速度非常快,比如太快了Stopwatch,因此绘制渐变不会那么难.

我的建议是尝试缓存一些东西.打开画图,绘制角落并保存到磁盘,或仅在内存中生成一次图像.然后根据需要加载(并调整大小).渐变相同.

即使不同的按钮具有不同的颜色,但具有相同的图案,您也可以使用Paint或其他任何东西创建位图,并在运行时加载它并将Color值乘以另一种颜色.

编辑:

如果我们在循环之前暂停布局面板并在循环结束时恢复面板的布局

这不是SuspendLayout和ResumeLayout的用途.它们暂停布局逻辑,即控件的自动定位.与FlowLayoutPanel和TableLayoutPanel最相关.

至于doublebuffering,我不确定它是否适用于自定义绘制代码(尚未尝试).我想你应该实现自己的.

简而言之,双重缓冲: 它非常简单,只需几行代码.在paint事件上,渲染到位图而不是渲染到Graphics对象,然后将该位图绘制到Graphics对象.


Ada*_*son 4

除了该DoubleBuffered属性之外,还可以尝试将其添加到控件的构造函数中:

SetStyle(ControlStyles.OptimizedDoubleBuffer | 
         ControlStyles.AllPaintingInWmPaint, true);
Run Code Online (Sandbox Code Playgroud)

如果这最终还不够(我会冒险说还不够),请考虑看看我对这个问题的回答,并暂停/恢复面板或表单的重绘。这将使您的布局操作完成,然后在完成后进行所有绘图。