Welcome to WindowsClient.net | Sign in | Join

Here are some frequently asked questions about Windows Forms and their answers.

Windows Forms FAQs

What are some things that affect Layout performance?

The section lists out common performance problems and suggested solutions.

Changing the UI in OnLoad

Problem: Changing properties such as Bounds, Size, Location, Visible and Text/Image/etc for AutoSized controls InitializeComponent and/or Suspend/ResumeLayout can cause perf problems. Each time one of these properties are touched a layout is forced. Changing any of these in Form.Load is particularly bad, at that point the handles have all been created, and window messages have to be sent to change size/locations.

Solution: Add a Suspend/Resume Layout to prevent extra layouts from occurring. If possible, make all of these changes within InitializeComponent – this way only one layout is ever needed.

Visual Inheritance

Problem: The code generated for visual inheritance (class Form2 : Form1) is actually not that efficient, as the constructor for the base form executes, then the constructor for the derived form executes. This essentially forces two layouts as ResumeLayout is called on the form twice. Visual Inheritance also has problems for localization, as the localization tools dont work well with this kind of situation.

Solution: For perf reasons, if you can avoid using it, this is the best solution. If you must, you may want to consider using a base form and swapping in a panel with the derived controls as needed.

Setting the Size/Location of child controls in the Resize event.

Problem: Using the Resize event to size/position child controls circumvents the Suspend/Resume Layout perf protection.

Solution: Use the Layout event to size/position child controls.

  private void button1_Click(object sender, EventArgs e) {

   // Inefficient Layout practice
   this.Resize += new System.EventHandler(this.Form1_Resize);
   this.SuspendLayout(); 
   this.Size = new Size(500, 500);  // SuspendLayout circumvented!     
   this.ResumeLayout(false);
   this.Resize -= new System.EventHandler(this.Form1_Resize);

   // Efficent Layout pracitce
   this.Layout += new LayoutEventHandler(Form1_Layout);
   this.SuspendLayout();
   this.Size = new Size(500, 500);
   this.ResumeLayout(false);
   this.Layout -= new LayoutEventHandler(Form1_Layout);

  }

  void Form1_Layout(object sender, LayoutEventArgs e) {
   // for added performance, consider storing off the last size 
   // that happened when we got here and only perform the layout if the size has changed.
   Rectangle bounds = this.ClientRectangle;
   bounds.Inflate(-10, -10);
   outerPanel.Bounds = bounds;
   this.Text = "Resized in LAYOUT!";
  }

  private void Form1_Resize(object sender, EventArgs e) {
   Rectangle bounds = this.ClientRectangle;
   bounds.Inflate(-10, -10);
   outerPanel.Bounds = bounds;
   this.Text = "Resized on RESIZE!";
  }
Using nested AutoSized table layout panels more than two deep.

Problem: The tablelayoutpanel is a great control for automatically controlling layout, however it has its price performance wise. The simpler you can make the layout, the faster it will be.

Solution: Keep the complexity of your layout to a minimum. If the flowlayoutpanel can achieve the same results for your UI, consider tatically using that in places. A flow is always mathematically cheaper to calculate than a table.

Setting the Font after InitializeComponent is complete.

Problem: The font controls the auto-scaling mechanism for the form. Setting this after the form has finished laying out will cause it to perform an expensive layout all over again, changing the size/location of child controls.

Solution: If it knows this data before the final resume layout of initialize component, it will have all the data it needs to perform the layout efficiently, ONCE.

Dynamically filling in data

Problem: Changing the text of child controls can cause layouts.

Solution: Call SuspendLayout on the parent control first, then set the text of all the controls, then call ResumeLayout. If this can be done before the handles are created, it will be even faster.

Individually setting the Size/Location of a control in multiple properties.

Problem: There are several properties in which you can change the size/location of a control. These include, but are not limited to: Width, Height, Top, Bottom, Left, Right, Size, Location, Bounds, and ClientSize. Setting panel1.Width = 10 and panel1.Height = 10 causes twice the work to occur than setting them both together (especially after the handle has been created as windows forms is chatting live with the operating system about what the size should really be.)

Solution: Do all your calculations, then set the property that reflects the most information you have. If you're just chaning the Size, set Size, if you're changing the Size and Location change Bounds.

  private void button1_Click(object sender, EventArgs e) {
   Stopwatch sw = new Stopwatch();

   sw.Start();
   for (int i = 1; i < 1000; i++) {
    panel1.Width = i;
    panel1.Height = i;
   }
   sw.Stop();
 
   Stopwatch sw2 = new Stopwatch();
   sw2.Start();
   for (int i = 1; i < 1000; i++) {
    panel1.Size = new Size(i, i);
   }
   sw2.Stop();
   MessageBox.Show(String.Format("Trial 1 {0}\r\nTrial 2 {1}", sw.ElapsedMilliseconds, sw2.ElapsedMilliseconds));
  }

  // As expected, the second one takes about 1/2 the time because there's half the conversation going on.
  // Trial 1 3717
  // Trial 2 1865
Clearing a controls collection to swap views

Problem: Someone has a form which has a panel which swaps in and out. When swapping panels, Controls.Clear() is used to clear off the old panel.

This is inefficient as it:

  • Forces a layout - the Panel's parent is set to null, which causes a ParentChanged, which results in a layout
  • Leaks resources – as the Panel is no longer associated with the form and will hold open USER32 handles.

Solution: There are a couple of solutions, depending if you're done with the panel or not.

  • If you're not done with the panel – just set it to be Visible=false
  • If you're done with the panel – call dispose on it.


Page view counter