C# Windows 窗体:使用 BackgroundImage 会减慢窗体控件的绘制速度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/778095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 00:46:16  来源:igfitidea点击:

Windows Forms: using BackgroundImage slows down drawing of the Form's controls

c#.netwinformsperformance

提问by steffenj

I have a Windows Form (C# .NET 3.5) with a number of buttons and other controls on it, all assigned to a topmost Panel which spans the whole Form. For example, the hierarchy is: Form -> Panel -> other controls.

我有一个 Windows 窗体(C# .NET 3.5),上面有许多按钮和其他控件,所有这些按钮和其他控件都分配给跨越整个窗体的最顶层面板。比如层次结构是:表单->面板->其他控件。

As soon as I assign a BackgroundImage to the Panel, the controls draw very slowly. I have the same effect if I use the Form's BackgroundImage property and set the Panel's BackgroundColor to "transparent". It appears as if the window with the background is drawn first, then each control is added one-by-one each with a slight delay before the next is drawn. In other words, you can actually follow the order in which each control is drawn to the Form. Once all Controls have been drawn once this effect doesn't happen anymore but the responsiveness of the Form is still slow.

一旦我将 BackgroundImage 分配给 Panel,控件的绘制速度就会非常缓慢。如果我使用 Form 的 BackgroundImage 属性并将面板的 BackgroundColor 设置为“透明”,我也有同样的效果。看起来好像首先绘制带有背景的窗口,然后在绘制下一个控件之前一个一个地添加每个控件,每个控件都有轻微​​的延迟。换句话说,您实际上可以按照每个控件绘制到 Form 的顺序进行操作。一旦绘制了所有控件,这种效果不再发生,但表单的响应速度仍然很慢。

In Visual Studio's designer I get the same effect, especially noticeable when moving controls around. Sometimes the form's drawing stops completely for a second or two which makes working with BackgroundImage a total drag, both in the designer and the resulting application.

在 Visual Studio 的设计器中,我得到了相同的效果,在四处移动控件时尤其明显。有时窗体的绘制会完全停止一两秒钟,这使得在设计器和生成的应用程序中使用 BackgroundImage 完全是一种拖累。

Of course, I tried using DoubleBuffered = true, and I also set it on all controls using reflection, to no effect.

当然,我尝试使用 DoubleBuffered = true,并且我还使用反射在所有控件上设置了它,但没有任何效果。

Also, here's the forms loading code because it's a bit unusual. It copies all controls from another form onto the current form. This is done in order to be able to edit each screen's visual appearance separately using the designer while sharing a common form and common code basis. I have a hunch that it may be the cause of the slowdowns, but it still doesn't explain why the slowdowns are already noticeable in the designer.

另外,这里是表单加载代码,因为它有点不寻常。它将所有控件从另一个窗体复制到当前窗体上。这样做是为了能够使用设计器单独编辑每个屏幕的视觉外观,同时共享公共形式和公共代码基础。我有一种预感,这可能是减速的原因,但它仍然无法解释为什么设计器中的减速已经很明显了。

private void LoadControls(Form form)
{
    this.SuspendLayout();

    this.DoubleBuffered = true;
    EnableDoubleBuffering(this.Controls);

    this.BackgroundImage = form.BackgroundImage;
    this.BackColor = form.BackColor;

    this.Controls.Clear();
    foreach (Control c in form.Controls)
    {
        this.Controls.Add(c);
    }

    this.ResumeLayout();
}

As you can see, SuspendLayout()and ResumeLayout()are used to avoid unnecessary redraw.

如您所见,SuspendLayout()ResumeLayout()用于避免不必要的重绘。

Still, the form is "slow as hell" once a BackgroundImage is used. I even tried converting it to PNG, JPG and BMP to see if that makes any difference. Also, the image is 1024x768 in size, but smaller images have the same slowdown effect (although slightly less).

尽管如此,一旦使用了 BackgroundImage,该表单就会“慢得要命”。我什至尝试将其转换为 PNG、JPG 和 BMP,看看是否有什么不同。此外,图像的大小为 1024x768,但较小的图像具有相同的减速效果(虽然略小)。

What should I do?

我该怎么办?

回答by Adam Robinson

SuspendLayout()and ResumeLayout()do not suspend drawing, only layout operations. Give this guy a shot:

SuspendLayout()并且ResumeLayout()不暂停绘图,只进行布局操作。给这个人一个机会:

public static class ControlHelper
{
    #region Redraw Suspend/Resume
    [DllImport("user32.dll", EntryPoint = "SendMessageA", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
    private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
    private const int WM_SETREDRAW = 0xB;

    public static void SuspendDrawing(this Control target)
    {
        SendMessage(target.Handle, WM_SETREDRAW, 0, 0);
    }

    public static void ResumeDrawing(this Control target) { ResumeDrawing(target, true); }
    public static void ResumeDrawing(this Control target, bool redraw)
    {
        SendMessage(target.Handle, WM_SETREDRAW, 1, 0);

        if (redraw)
        {
            target.Refresh();
        }
    }
    #endregion
}

Usage should be pretty self-explanatory, and the syntax is identical to SuspendLayout()and ResumeLayout(). These are extension methods that will show on any instance of Control.

用法应该是不言自明的,语法与SuspendLayout()和相同ResumeLayout()。这些是将显示在任何 Control 实例上的扩展方法。

回答by Adam Robinson

For me, the post Form load is slow if adding a background imagesolved the problem:

对我来说,如果添加背景图像解决了问题,则表单加载很慢

Make sure your backgroundcolor is not set to 'transparent'. Set it to 'white' for better performance.

Also, do BackgroundImageLayoutto be either 'Center' or 'Stretch' to increase performance. This will enable the double buffer on the form.

确保您的背景颜色未设置为“透明”。将其设置为“白色”以获得更好的性能。

此外,将BackgroundImageLayout设为“Center”或“ Stretch”以提高性能。这将启用表单上的双缓冲区。

回答by Sheenit Mathew

I also faced the same problem and could solve it by reducing the resolution of the background picture. When you use big sized (eg:1280X800) pictures as the background, it will take time to draw controls on the form. It is better to open the picture in 'Paint' re size it smaller than your form and then save in 'bmp' format. Now try to add this picture as your form's background.

我也遇到了同样的问题,可以通过降低背景图片的分辨率来解决。当您使用大尺寸(例如:1280X800)图片作为背景时,在窗体上绘制控件需要时间。最好在“Paint”中打开图片,将其大小调整为比您的表单小,然后以“bmp”格式保存。现在尝试将此图片添加为表单的背景。

回答by Martin M?ller

Another very simple way to avoid permanent redraws while adding your controls is to make the parent control invisible before you add controls to it. Afterwards you make the parent control (for example, a panel) visible and there it is without all those repaints. :-)

添加控件时避免永久重绘的另一种非常简单的方法是在添加控件之前使父控件不可见。之后,您使父控件(例如,面板)可见,并且没有所有这些重绘。:-)

panelParent.visible = false;

for(...) {
    // Add your controls here:
    panelParent.Controls.Add(...);
}

panelParent.visible = true;

回答by user6091473

I realize this is an old thread, but I found it while searching for info on the same issue, so in case it's useful to someone at some point:

我意识到这是一个旧线程,但我在搜索有关同一问题的信息时发现了它,因此以防万一它在某些时候对某人有用:

My situation: I have a 13 x 12 grid of panels, which have a background image set dynamically, and regularly changed, based on user selections. Each Panel also has a text label control added to it. In order for the text to overlay the background image, it has to be set to Transparent (btw - my experience was that BackgroundImageLayout of Zoom, Stretch, Center had little to no effect. BackColor set to transparent or white also had little effect).

我的情况:我有一个 13 x 12 的面板网格,它有一个动态设置的背景图像,并根据用户选择定期更改。每个面板还添加了一个文本标签控件。为了让文本覆盖背景图像,它必须设置为透明(顺便说一句 - 我的经验是缩放、拉伸、中心的 BackgroundImageLayout 几乎没有影响。BackColor 设置为透明或白色也几乎没有影响)。

Each draw of the grid of panels (including resizing) was taking about a second - not bad but very visible, and a usability issue on slower machines.

每次绘制面板网格(包括调整大小)大约需要一秒钟 - 不错但非常明显,并且在较慢的机器上存在可用性问题。

My images were not huge,but somewhat oversized.

我的图像不是很大,但有点过大。

What I found: By resizing my set of images to the exact panel size before setting the backgroundimage, the draw time went down dramatically - somewhere around 0.1 seconds. Since my program dynamically resizes panels based on windows size, I dynamically resize the set of images one time on windows resize event before setting the background of the 156 panels.

我的发现:通过在设置背景图像之前将我的图像集调整为精确的面板大小,绘制时间显着减少 - 大约 0.1 秒。由于我的程序会根据窗口大小动态调整面板大小,因此在设置 156 个面板的背景之前,我会在窗口调整大小事件上一次动态调整图像集的大小。

In hindsight, its an obvious optimization... resize 8 images once instead of repeatedly 156 times.

事后看来,这是一个明显的优化……一次调整 8 张图像的大小,而不是重复 156 次。

回答by Pavel

I solved same problem using PictureBox. Just add PictureBoxto your container, choose "Dock it in parent container" (or property Dock= Fill) and set it's Image. It will looks just like BackGroundImage of the parent control.

我使用PictureBox. 只需添加PictureBox到您的容器中,选择“将其停靠在父容器中”(或 property Dock= Fill)并将其设置为 Image。它看起来就像父控件的 BackGroundImage。