Windows窗体:使用背景调光会减慢窗体控件的绘制速度

时间:2021-07-10 16:03:03

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),上面有许多按钮和其他控件,都被分配给一个覆盖整个窗体的最顶端面板。例如,层次结构是:Form ->面板->其他控件。

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.

一旦我把背景调到面板上,控制就会很慢。如果我使用表单的背景属性并将面板的背景颜色设置为“透明”,那么我也有同样的效果。它似乎是先绘制具有背景的窗口,然后在绘制下一个控件之前,逐个添加每个控件,并稍微延迟。换句话说,您实际上可以按照每个控件绘制到窗体的顺序进行操作。一旦所有的控件都被绘制出来,这种效果就不会再发生了,但是表单的响应速度仍然很慢。

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.

如您所见,挂起布局()和恢复布局()用于避免不必要的重新绘制。

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).

不过,一旦使用了背景知识,这种形式就会“像地狱一样缓慢”。我甚至试着把它转换成PNG、JPG和BMP,看看会不会有什么不同。此外,图像大小为1024x768,但较小的图像具有相同的减速效果(尽管稍微小一些)。

What should I do?

我应该做什么?

6 个解决方案

#1


37  

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

挂起布局()和恢复布局()不挂起绘制,只是布局操作。给这个人一个机会:

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()和elayout()是相同的。这些扩展方法将显示在控件的任何实例上。

#2


5  

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)的图片作为背景时,在窗体上绘制控件需要时间。最好是在“绘制”中打开图片,把它的大小调整到比窗体小,然后保存为“bmp”格式。现在尝试添加这张照片作为你的表单的背景。

#3


2  

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;

#4


2  

I solved same problem using PictureBox. Just add PictureBox to 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添加到容器中,选择“停靠在父容器中”(or property Dock = Fill)并设置它的Image。它看起来就像父控件的背景。

#5


1  

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

对于我来说,如果增加一个背景图像就可以解决这个问题:

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

确保你的背景颜色没有设置为“透明”。设置为“白色”以获得更好的性能。

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

此外,要做一些“中心”或“拉伸”来提高性能。这将启用表单上的双重缓冲区。

#6


1  

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×12的面板网格,其中有一个动态的背景图像集,并根据用户的选择定期更改。每个面板还添加了一个文本标签控件。为了让文本覆盖背景图像,它必须设置为透明(btw -我的经验是,缩放、拉伸、中心的背景设置几乎没有效果。背景色设置为透明或白色也没有什么效果)。

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个面板的背景之前,我在windows resize事件上一次动态调整图像集的大小。

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

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

#1


37  

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

挂起布局()和恢复布局()不挂起绘制,只是布局操作。给这个人一个机会:

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()和elayout()是相同的。这些扩展方法将显示在控件的任何实例上。

#2


5  

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)的图片作为背景时,在窗体上绘制控件需要时间。最好是在“绘制”中打开图片,把它的大小调整到比窗体小,然后保存为“bmp”格式。现在尝试添加这张照片作为你的表单的背景。

#3


2  

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;

#4


2  

I solved same problem using PictureBox. Just add PictureBox to 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添加到容器中,选择“停靠在父容器中”(or property Dock = Fill)并设置它的Image。它看起来就像父控件的背景。

#5


1  

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

对于我来说,如果增加一个背景图像就可以解决这个问题:

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

确保你的背景颜色没有设置为“透明”。设置为“白色”以获得更好的性能。

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

此外,要做一些“中心”或“拉伸”来提高性能。这将启用表单上的双重缓冲区。

#6


1  

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×12的面板网格,其中有一个动态的背景图像集,并根据用户的选择定期更改。每个面板还添加了一个文本标签控件。为了让文本覆盖背景图像,它必须设置为透明(btw -我的经验是,缩放、拉伸、中心的背景设置几乎没有效果。背景色设置为透明或白色也没有什么效果)。

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个面板的背景之前,我在windows resize事件上一次动态调整图像集的大小。

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

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