代码之家  ›  专栏  ›  技术社区  ›  steffenj

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

  •  26
  • steffenj  · 技术社区  · 17 年前

    我有一个Windows窗体(C#.NET 3.5),上面有许多按钮和其他控件,它们都被分配给一个覆盖整个窗体的最上面的面板。例如,层次结构为:窗体->面板->其他控件。

    一旦我将背景图像分配给面板,控件就会非常缓慢地绘制。如果我使用窗体的BackgroundImage属性并将面板的BackgroundColor设置为“透明”,那么我也有同样的效果。看起来好像是先绘制带有背景的窗口,然后在绘制下一个控件之前,每个控件都会逐个添加,并稍微延迟。换句话说,您实际上可以遵循每个控件被绘制到窗体的顺序。一旦所有控件都被绘制出来,一旦这种效果不再发生,但是表单的响应仍然很慢。

    在Visual Studio的设计器中,我得到了同样的效果,尤其是在移动控件时。有时表单的绘图会完全停止一两秒钟,这使得在设计器和生成的应用程序中使用BackgroundImage完全是一种阻力。

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

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

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

    如你所见, SuspendLayout() ResumeLayout() 用来避免不必要的重画。

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

    我该怎么办?

    6 回复  |  直到 13 年前
        1
  •  43
  •   Adam Robinson    17 年前

    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
    }
    

    用法应该很简单,语法与 挂起布局() ResumeLayout() . 这些是将在控件的任何实例上显示的扩展方法。

        2
  •  5
  •   Artemix    13 年前

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

        3
  •  3
  •   Peter Mortensen Pieter Jan Bonestroo    13 年前

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

    panelParent.visible = false;
    
    for(...) {
        // Add your controls here:
        panelParent.Controls.Add(...);
    }
    
    panelParent.visible = true;
    
        4
  •  2
  •   Pavel    9 年前

    我解决了同样的问题 PictureBox . 只需添加 图片框 对于容器,选择“将其停靠在父容器中”(或属性 Dock = Fill )设定它的图像。它看起来就像父控件的BackGroundImage。

        5
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    13 年前

    对我来说 Form load is slow if adding a background image 解决了问题:

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

    还有,做 BackgroundImageLayout 要么是“中心”要么是 Stretch “以提高绩效。这将启用窗体上的双缓冲区。

        6
  •  1
  •   user6091473    10 年前

    我意识到这是一条老线索,但我是在搜索同一问题的信息时发现的,所以万一在某个时候对某人有用:

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

    每次绘制面板网格(包括调整大小)都要花上大约一秒钟的时间——虽然不是很糟糕,但非常明显,而且在速度较慢的机器上也会出现可用性问题。

    我的照片不是很大,但有点太大了。

    我发现:在设置背景图像之前,通过将图像集大小调整为面板大小,绘制时间显著缩短了大约0.1秒。由于我的程序根据windows大小动态调整面板大小,所以在设置156个面板的背景之前,我在windows resize事件中动态调整一次图像集的大小。

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