There are two forms in my project and I am trying to add the controls of Form2's panel into Form1's panel.
So,
Form2 form2 = new Form2();
new_panel = form2.Controls["panel1"] as Panel; // form2's panel
this.panel.Controls.Add(new_panel); // add form2's panel into form1's panel.
And suddenly, the form2.Controls["panel1"] becomes NULL.
I can't understand why it happens.
A control can only have one parent - if you add a control which already has a parent to another control, it will remove itself first.
From the docs for ControlCollection.Add:
A Control can only be assigned to one Control.ControlCollection at a time. If the Control is already a child of another control it is removed from that control before it is added to another control.
If you think about it, that makes sense - a panel needs to know where it is, how big it is etc. It can really logically only be in one place at a time.
As an aside, I'd recommend using a cast rather than as when you're proceeding unconditionally as if the cast has worked - that way, if the relevant object isn't of the right type, you get an exception at the earliest moment of detection, instead of a null reference propagating itself through your system, possibly not being picked up for a long time (making it harder to diagnose the issue and introducing the possibility of data being lost).
Related
I just started working with Visual Studio C# and to be honest I didn't fully understand what happens when we chose to hide a form or a user control.
My intuition tells me this hide/show method is kind of "inefficient" way to get an user through all the functions of my app.
So I am asking you guys if there is another workaround to "load" user control parts in a form.
Right now my main_menu form has all the user control objects placed on the form, but hidden, and I am using buttons to show them.
Is there a better way to achieve the same result? (I was thinking of a workaround like having an empty panel where I can load the User Control - not sure if possible)
Thank you!
You can create the controls on the fly and add them to or remove them from the Controls collection. On the class level, define this field
private Control _currentPanel;
You can use a more specific type here, if you are deriving all your panels from a common base type.
Then change the panel with
// Remove previous one.
if (_currentPanel != null) {
Controls.Remove(_currentPanel);
}
// Add new one
_currentPanel = new MyNewPanel();
//TODO: possibly set the panels Docking property to Fill here.
Controls.Add(_currentPanel);
In the example I am working with the form's Controls collection; however, you might have to use the Controls collection of some container control holding the panel.
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.
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.
I have a program which creates new Controls (actually - Form's with TopLevel = false) and puts them on a Panel which is on Form1.
For some reason, this is usually followed by what seems like a Form appearing very briefly outside of Form1. It's hard to tell exactly what's happening because it's so brief. Perhaps it's Form1 moving there (and changing its size) and then returning. Or perhaps it's the Form that's being put on Form1 that appears there (though how could that happen at all? It's out of Form1!).
I couldn't find anything in the code that might cause it, and the whole code is way too long to post here. (The relevant code is simply: MyPanel.Controls.Add(newForm);.)
Is there any known bug that might cause this?
I can sort of guess what the code looks like. The problem with adding a Form to the Controls collection with TopLevel = false is that you have to explicitly make it visible. This is an odd quirk (aka bug), normally a control automatically becomes visible when you add it to a Controls collection.
So it probably looks like this:
var newform = new Form1();
newform.Visible = true; // or newform.Show()
newform.TopLevel = false;
MyPanel.Controls.Add(newForm);
And yes, that can make it visible for a very brief moment, still as a top-level window, if you do something else that causes messages to be dispatched. Fix it by swapping the Visible and TopLevel property assignments.
If this doesn't help then post a code snippet.
So you tried adding a Form to a Form's Controls collection and received the exception telling you that you can't add top level controls. To "fix" it, you set the TopLevel property of that Form to false.
That's not the correct fix.
Use a Panel (or one of its derivatives) instead.
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.