This MSDN article offers some good advice about manually customising the form designer to improve performance:
Reduce the number of method and property calls on controls during startup. For example, Controls.Bounds is a better option than calls to Control.Location and Control.Size.
Create the form from the top down. In nested control hierarchies, set the parent property of containers (using the above rule) before adding controls to the container. As in the BigForm application, the panels had their parent property set to the form before the 40 controls were connected to the panel. If further containers exist lower in the hierarchy, the same changes should be applied.
I have followed the 1st bit of advice, replacing:
this.MyControl.Location = new System.Drawing.Point(5, 5);
this.MyControl.Size = new System.Drawing.Size(630, 90);
with:
this.MyControl.Bounds = new System.Drawing.Rectangle(5, 5, 630, 90);
This resulted in a super 20% (about 200ms) speed-up on one form. I'm trying to follow the 2nd bit of advice and not quite sure how to proceed. The Designer.cs file contains code like this:
this.Controls.Add(this.pnlHeader);
but not the code I was expecting (according to the example):
this.pnHeader.Parent = this; // Not in the Designer
The code this.Controls.Add(this.pnlHeader); appears at the bottom of InitializeComponent. Is the advice suggesting moving the code to the top or something else entirely?
EDIT #2
i found this msdn article which explain the issue as following:
Another method for improving performance is to initialize the controls
in the control tree top-down. For example, if you have a panel control
with many controls in it, create the panel first, and then add the
controls to the panel. Also, setting the parent property of the
control instead of adding to the Controls collection can improve
performance.
For example, consider adding a textbox to a panel's control collection:
Before optimization:
// Create a new panel and textbox control
Panel panel1 = new Panel();
TextBox textBox1 = new TextBox();
// Set the Text property of the TextBox control
textBox1.Text = "My Text";
// Add the TextBox to the Panel's control collection
panel1.Controls.Add(this.textBox1);
// Add the Panel to the Form's control collection
this.Controls.Add(panel1);
//... subsequent controls
Optimizing this code snippet using the top-down and parenting techniques results in the following snippet:
After optimization:
// Create a new panel and textbox control
Panel panel1 = new Panel();
TextBox textBox1 = new TextBox();
// set parents from top to down
this.panel1.Parent = this;
this.textBox1.Parent = this.panel1;
// Set properties of child control (cause repainting only once)
textBox1.Text = "My Text";
//... subsequent controls
This can make a big difference with a deeply nested control hierarchy.
Optimizing the code in the InitializeComponent method by creating
the controls top-down and re-parenting them resulted in a performance
improvement of about 50% over the default Forms Designer generated
code!
Following on from the answer from S.Serp here are some observations to be aware of:
Replacing Location and Size with Bounds resulted in an average boost of ~15% for form loading.
Replacing Controls.Add with Parent resulted in a further boost of ~5-10% (for an impressive total of ~20-25%).
As Sefe points out, manually editing the Designer.cs file is usually only appropriate where you are not making changes in the Designer very often. Any changes made in the Designer will overwrite your manual code. Be warned! This is not a disaster, you simply lose the boost. Either live with the slower form loading or redo the manual changes.
Be careful to put the Bounds call after setting Multiline = true; on TextBox controls (if enabled). If you set it before, your control will (unhelpfully) be resized to a single line.
Be careful with ensuring each child control has the parent set correctly! Open all forms in the Visual Studio Designer after manually editing InitializeComponent to see that everything is kosher...but don't edit anything otherwise your changes will be deleted.
The lines...
this.Controls.Add(this.pnlHeader);
...and...
this.pnHeader.Parent = this;
...are equivalent. The control trees they produce are identical. And you should not change the auto-generated code. Your changes will be overwritten the next time the designer writes its updates.
You usually don't have to worry about the designer-generated code. You can concentrate on your part and assume the designer is correct.
Related
I want to load Form's controls to a panel in C# so the panel will show the same components as the form. I have tried this code:
foreach (Control control in (new Form2()).Controls)
{
panels[panelsCounter].Controls.Add(control);
}
But the problem is that when I'm running the program it loads only the type of control that I've added last (For example if I've been added a label and than I've added a button to the form it shows only a button, but if I add another label, it shows both of the labels, but not the button).
Please help me.
This is a classic bug, you are modifying the collection while you are iterating it. The side-effect is that only ever other control will be moved to the panel. You'll need to do this carefully, iterate the collection backwards to avoid the problem:
var formObj = new Form2(); //???
for (int ix = formObj.Controls.Count-1; ix >= 0; --ix) {
panels[panelsCounter].Controls.Add(formObj.Controls[ix]);
}
Controls are not designed to be displayed multiple times. You cannot add controls to multiple forms, or add the same control to a form multiple times. They simply weren't designed to support it.
You could go through each control and create a new control of the same type, and even copy over the values of their properties (or at least what's publicly accessible to you), effectively cloning them, but it's important that it be a different control that you add to the new panel.
m developing a sort of project which I needed a cool customizable interface, so I designed a 'parent-form' from which all childs would get 'stylized', according to XML customization options.
I added a TableLayoutPanel to draw borders and a Panel in the middle, where child forms would supposedly add their components and make their jobs.
The problem I face is, even though I set that 'content panel' to 'public', the designer wont let me add controls to it from the child forms.
Is there any different way I can make designable forms deriving from a 'customizable' superclass?
Edit: The parent class is public, every container containing the Content-Panel are also set to public.
I manually added to child's designer.cs a new Panel inside the parent's content pane, set it to DockStyle.Fill. When I came back to the Designer, it will now let me add components to child's content Panel.
A bit messy and I'm pretty sure there shall be another way around...
But I'll work along like this until i can figure out a better workaround.
I have added a new public Panel from code other than designer in the parent's class scope, Then in the parent constructor I added it to the TableLayoutPanel, configured docking and colspan from constructor code, below InitializeComponents() call and BAM!
public Panel contentPane = new Panel();
public Dialogo()
{
InitializeComponent();
Content.Controls.Add(contentPane);
contentPane.Dock = DockStyle.Fill;
// More code
}
So its a contentPane inside 'Content' which is another panel in the second line of the table ocupying 5 columns (so the table surrounds it and allows me to draw the borders around.
I don't know why, but having added the content-panel in code other than on the designer allowed me to directly add components to the panel from the Designer in child forms.
I have a C# Forms tab application. Each TabPage has a menu on the left (Outlook style navigation panel), and a Panel on the right for content.
If I want the content panel for tab page 0, how would I go about fetching it? I'm a bit stumped because I don't know how to index into the controls collection on a tab page. The following is underlined in red, so I believe its wrong.
Panel panel = tabControl.TabPages[0].Controls["Panel"];
EDIT: remove Window in Panel sub question. It will be moved to a separate question.
Sorry about the beginner questions. I'm a C/C++ guy with lots of MFC time, and C# UI is a bit frustrating at the moment.
foreach (Control control in tabControl1.TabPages[0].Controls)
{
// if (control.Name == "panel1")
}
You can always call this recursively on control.Controls to find a control in any hierarchy. control.Name can be used to find your specific control.
You can't show a Form, inside a Panel. You could create Custom Control where you can add your functionality and add that control to a Panel.
in order to create a new form for example you need to create a variable of what ever form that it is you want to create.
example
Form2 frm2 = new Form2();
frm2.Show();
if you want to show that form in the panel then the panel would be the Owner keep in mind the difference between Owner and Parent
please paste what ever code you have so far and we can suggest the necessary changes
Finally, how does one display a Window in a Panel? - you don't want to do that. If you want a window and a panel to share a piece of UI functionality, create a user control with all the the functionality and then you can place it in a form or in a panel.
A possibility to encapsulate complex UI content is to create a UserControl. This way you can create a reusable piece of complex UI you can basically add as a "blob" inside a form.
The reason why
Panel panel = tabControl.TabPages[0].Controls["Panel"];
is underlined red is because the Controls collection returns a Control which might be a Panel but also might be something else. So you need to cast it:
Panel panel = tabControl.TabPages[0].Controls["Panel"] as Panel;
if (panel != null)
{
// got a panel here so do something
}
Also: MSDN has some good resources - you should make use of it.
This is really simple.
I have a TableLayoutPanel that is populated with controls (just Labels, Buttons, and some Panels with buttons) based on a database query. When the data needs to be refreshed, I use TableLayoutPanel.Controls.Clear(). Unfortunately, this is a very slow operation. I would expect it to be faster than the code populating the table, but it is at least 3 or 4 times slower.
I definitively proved that the slowness is when executing Controls.Clear() by executing this as the single thing done to the TableLayoutPanel after a message box is displayed (then the procedure returns). The controls visibly disappear from the bottom up. When the recordset is used to repopulate the TableLayoutPanel, the speed of the controls appearing from top to bottom is almost faster than I can see.
I'm already doing TableLayoutPanel.SuspendLayout() and ResumeLayout().
Using this.DoubleBuffered = true on the form doesn't appear to do anything.
I could just Dispose the entire control and recreate it through code, but this is a big pain and makes having a nice form designer GUI pointless. I would have to dig into every property I've set on the control and create a line of code for it (though I guess I could get this out of the designer code itself, it still feels wrong).
Any ideas on how to do the job faster? I'm even open to using other methods besides a TableLayoutPanel... I just need the freedom to put multiple buttons per cell or barring that to be able to span columns in the table header.
Can C# at least freeze the whole form while it redraws and then paint all at once?
I've run into issues with slowness using TableLayoutPanels as well. Rather than setting the DoubleBuffered property on the form, the best solution I have found is to create a new class that inherits from TableLayoutPanel, and in that class' constructor, enable double-buffering:
public class DoubleBufferedTableLayoutPanel : TableLayoutPanel
{
public DoubleBufferedTableLayoutPanel()
{
DoubleBuffered = true;
}
}
Then, use the DoubleBufferedTableLayoutPanel wherever you would normally use a TableLayoutPanel.
This seems to work for my uses:
tableLayoutPanel.Visible = false;
tableLayoutPanel.Clear();
/* Add components to it */
tableLayoutPanel.Visible = true;
There is no need to subclass TableLayoutPanel as in Chris Ryan's answer. I had the same problem and solved it by setting the property through reflection:
typeof(TableLayoutPanel)
.GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.SetValue(myTableLayoutPanel, true, null);
If i'm going to built up some dynamic gui i'm always going to do so in code. But at a starting point i just start with the designer on a dummy form and style each control the way i (or better the customer) like(s). Afterwards i take a look into the Designer.cs file and copy the necessary property settings out of it into some factory function like
private TextBox CreateTextBox(string name, /* maybe other parameters */)
{
var textBox = new TextBox();
textBox.Name = name;
//Other settings from given parameters...
//Further settings which are all the same for these kind of control
textBox.KeyDown += (sender, e) => {};
return textBox;
}
So i make sure that every control feels and looks the same on my GUI. This will be done on each level within my surface (starting with the small controls like TextBox and goes up to the containers like GroupBox or TableLayoutPanel.
In some cases this leads to a point where a factory function calls several other factory functions. If this is becoming true it's time to think about encapsulating these controls into a single UserControl, but as always it depends if this is needed or not.
From my side i can only encourage you to move your code out of the designer into a self-written function. At the beginning it is (as always) more work, but afterwards it is easier to make even bigger changes to the layout.
I have a complex Windows Forms GUI program that has a lot of automated control generation and manipulation. One thing that I need to be able to do is add a custom UserControl to a newly instatiated TabPage. However, when my code does this I get automatic resizing events that cause the formatting to get ugly. Without detailing all of the different Containers that could possibly be involved, the basic issue is this:
At a certain point in the code I create a new tab page:
TabPage tempTabPage = new TabPage("A New Tab Page");
Then I set it to a certain size that I want it to maintain:
tempTabPage.Width = 1008;
tempTabPage.Height = 621;
Then I add it to a TabControl:
tabControl.TabPages.Add(tempTabPage);
Then I create a user control that I want to appear in the newly added TabPage:
CustomView customView = new CustomView("A new custom control");
Here is where the problem comes in. At this point both the tempTabPage and the customView are the same size with no padding or margin and they are the size I want them to be. I now try to add this new custom UserControl to the tab page like this:
tempTabPage.Controls.Add(customView);
When making this call the customView and it's children controls get resized to be larger and so parts of the customView are hidden.
Can anyone give me any direction on what to look for or what could be causing this kind of issue?
Thanks ahead of time.
The UserControl's "AutoScaleMode" property should be set to "None".
If you want the customView to fill the TabPage.
Use Dock like this:
tempTabPage.Controls.Add(customView);
customView.Dock = DockStyle.Fill;
Then the customView will fill out the space in the TabPage, but you have to handle resizing of the customView so child controls will be shown properly.
I had the same issue.
The UserControl's "AutoScaleMode" set to "None" works for me.