If I create a winForms "myForm" then the following boiler plate code is generated:
public partial class myForm: Form
{
public myForm()
{
//<<position 1
InitializeComponent();
//<<position 2
}
}
If I add extra code to the constructor method does it make any difference to the running of the app if I place my code in position 1 or 2 ?
Yes, it does.
InitializeComponent is the method that VS generates that is responsible for creating and positioning the controls on a form.
Code in "position 1" will execute before the controls exist. If you try to access a control in this position, you'll get a NullReferenceException (say, if you try to set the content of a TextBox). Similar code in "position 2" will work as expected.
There is use to "position 1" though: if you have custom controls or behaviour that rely on properties of your form, setting those properties in "position 1" might prevent that code from having to refresh if you allow controls to be created before those values are set.
Yes. Before InitializeComponent all controls have not been created yet.
in the function InitializeComponent(); you create all of your objects you put in the graphical layout. if you want to access them, your code should be in position2.
The button,textbox,combobox that you put on the Form automatically creates some code.This code is generated in InitializeComponent()
So calling the controls that you put on Form from position 1 is wrong because the controls are never created at that point of code.
You can call and access the controls that you put on the Form from position 2
Like the others here have said, position 2 should be used in most cases. However, there are exceptions where neither of the positions will work, and you will actually have to attach your code to another event.
Example: You have a ScrollViewer and an Image inside it. You insert a big image file in the Image component and want to show it so the center of the image is visible at the start, with an option to scroll around to see the rest of the image:
scrollViewer1.ScrollToVerticalOffset(scrollViewer1.ScrollableHeight / 2);
However writing this in the constructor either before or after InitializeComponent() (or even in) will not give you the result you want. (Because, even though the components have been initialized at this stage, the contained image file has not.) Instead what you have to do, is attach a new Event Handler ImageOpened to the Image and write the command there.
Related
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 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 am working on windows application form. I have a CustomControl (say MasterControl) on which i put a split panel and now my MasterControl is split into three parts say:
Pannel1
Pannel2
Pannel3
Now i develop three custom controls and put one in each of pannels e.g
Pannel1 have CustomControl1
Pannel2 have CustomControl2
Pannel3 have CustomControl3
Now somewhere in CustomControl3 I need to access a public member of CustomControl1. For which i wrote the following code:
((MasterControl)this.Parent)._oCustomControl1.PublicMember = this.PublicMember;
The code above doen't work in my case. When this line of code is executed in debug mode then a message box appears and states that "There is no code available for current location"
It's a really bad design for your controls to depend on how are the arranged on the parent container.
e.g. inside your third control, you are quering the property of the first one by accessing it from the parent, and then it's child control by name.
Your code will break very easily, if it can be compiled at all - I think the problem you're having is the order of compilation: in order for your parent form to be compiled, it needs to have child user controls finished. On the other hand the user controls you created need to have finished form.
It would be far better to set whatever behaviour you're after from the container of those controls - for example, by reacting to events from the control, and setting appropriate stuff on appropriate other controls (there are other ways as well ofcourse - the point is in the direction and flow of information - who's setting and using what).
If you have a split panel in your master control, you should go two levels up to find your master control:
((MasterControl)this.Parent.Parent)._oCustomControl1.PublicMember = this.PublicMember;
I found the answer by myself. I am positing here because it might help some one else.
The exact code is:
((MasterControl)this.Parent.Parent.Parent)._oCustomControl1.PublicMember = this.PublicMember;
Basically my coustomcontrol3 lies inside a split container panel, so when i wrote:
this.Parent then it points to Panel In which it is residing and if i wrote
this.Parent.Parent then it points to the spliter container in which above panel reside and if i wrote
this.Parent.Parent.Parent then it points to control in which this split container resides
I got the idea from "Farzin Zaker" answer, so thanks to him for his contribution
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 need to display a ListView in WinForms which should not have any lines between columns. I tried GridLines=false and also tried setting HeaderStyle to ColumnHeaderStyle.None. But this is not working. I want to remove the 2 vertical lines coming in the middle.
screenshot http://www.freeimagehosting.net/uploads/4bcee1f639.png
GridLine = False should work. (See Pramodh answer.) If not, you have something else wrong. If you setting this property in code, make sure the control's handle has been created. The method CreateControl and IsHandleCreated are helpful.
A common problem in C# that affects controls is accessing control properties in the form's constructor. Usually, the control handles are not created until they are "visible", which doesn't happen until the form is loaded. Therefore, if setting these properties in the form constructor, move the code to the Load event which is recommended by Microsoft.
Control.CreateControl Method
The CreateControl method forces a
handle to be created for the control
and its child controls. This method is
used when you need a handle
immediately for manipulation of the
control or its children; simply
calling a control's constructor does
not create the Handle.
CreateControl does not create a
control handle if the control's
Visible property is false. You can
either call the CreateHandle method or
access the Handle property to create
the control's handle regardless of the
control's visibility, but in this
case, no window handles are created
for the control's children.
just change the view into Details
and GridLines=false
like:
this.listView1.View = System.Windows.Forms.View.Details;
this.listView1.GridLines = false;