33

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.

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.

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

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

What should I do?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
steffenj
  • 7,967
  • 10
  • 35
  • 41
  • If you're using a version of VS with the profiler included, where does the profiler say you're spending all your time? – Greg D May 07 '09 at 17:04
  • possible duplicate of [How to fix the flickering in User controls](http://stackoverflow.com/questions/2612487/how-to-fix-the-flickering-in-user-controls) – Hans Passant Aug 19 '12 at 01:33

6 Answers6

47

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.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • it didn't help ... i also noticed that when popup menus overlay some of the button and force them to redraw, the redraw is particularly slow (eg you see the buttons border drawn, button filled with solid color, finally button draws it's picture, then follows the next button). – steffenj Apr 23 '09 at 08:30
  • This actually gave me a huge performance increase! I had about 60 labels on a single control, placed in a table layout panel. The columns was set to automatically resize, which took a lot of time to re-render. This trick seems to suspend the drawing, until the layout is complete, removing the annoying visible redraw. – Siewers Apr 20 '10 at 15:06
  • This made a big difference for my user_control as well. Thanks! – Paul H. Dec 30 '11 at 13:49
  • Wow, what a difference! Thank you Adam!! – Lee Richardson Sep 05 '12 at 17:50
  • Hi can you help me how to use this one? Thanks – MMakati Jul 28 '13 at 09:12
  • I found this solution very useful thank you. However after a bit of digging around there are some usability implications that you should be aware of when using WM_SETREDRAW in this manner (summary: while disabled the control can be "clicked-through" meaning other windows/controls hidden behind it might be triggered). See: http://fgaillard.com/2011/02/the-unfortunate-effect-of-wm_setredraw/ and also a implementation of an alternative http://blogs.msdn.com/b/oldnewthing/archive/2014/04/07/10514610.aspx – Sverrir Sigmundarson Nov 23 '15 at 16:08
  • It worked for me when suspending the drawing of the Parent control, not the control itself. – TH Todorov Mar 09 '16 at 09:10
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.

Artemix
  • 2,113
  • 2
  • 23
  • 34
  • I have tried so many things. And as a last one, I wanted give this a shot and it surprisingly worked for me. Significant difference... Thanks – curiousBoy Sep 14 '14 at 07:22
4

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;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
3

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.

Pavel
  • 5,374
  • 4
  • 30
  • 55
  • Can you elaborate on this a bit more? I am attempting this solution for a similar problem, except instead of adding controls, I am drawing on the form. The picturebox containing the image removes the lag when drawing, but now I no longer see my drawing graphics on the form. – Bobby Byrnes Mar 28 '18 at 16:03
  • @BobbyByrnes you can draw on picture as well (using `Paint` event for PictureBox) – Pavel Mar 28 '18 at 16:59
3

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.

Community
  • 1
  • 1
  • Even though my form had a fixed size to match the image size, the default BackgroundImageLayout of Tile was the culprit. Changed it to stretch and it fixed the problem. – Tim Jun 03 '14 at 22:22
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).

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.

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