473,508 Members | 2,363 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

User control constructor called twice

I'm new at developing user controls in C#, and one thing I've noticed
right off the bat is that the constructor gets called twice -- once at
design time, once at run time.

In short, I'm trying to develop a control derived from DataGridView. It
will have a default set of columns (but I don't want to create them via
the Properties window for various reasons) so I added a call to a
method AddColumns() in the constructor. But the columns end up getting
added twice -- once when I place the control in a form at design time,
and then again when I run the application.

I know that I can get around this by making an explicit call to
AddColumns() at runtime rather than putting it in the constructor. But
I like the fact that they show up at design time so that way I can see
what it's going to look like.

I tried adding an "initialized" flag in my constructor to prevent it
from callling AddColumns() twice, but that doesn't work because
apparently its design time value is reset at run time.

Any suggestions?

Nov 21 '06 #1
11 8112
Following up on my own message...

I thought that I could at least work around this problem by calling
Columns.Clear() in my AddColumns() method, but even though the column
count is zero after I clear them, two sets of the columns still show
up. So where is the other set being stored?

I think I'm missing some fundamental understanding about user controls.
Is the moral of the story to just not do anything in the constructor?

Nov 21 '06 #2
Hi,

The designer is creating an instance of your control at design-time. If it
didn't, you wouldn't see anything :)

When the designer creates your control, the constructor is executed and the
columns are added. Since the columns can be "designed", the designer
recognizes this and serializes code (I'm assuming) into your code-behind to
initialize the columns. I suspect that you see the columns in the grid even
at design-time, correct?

Of course, when you run the application the constructor executes and adds
the columns at runtime. This probably occurs in the InitializeComponent
method of your Form, also in which the code was serialized to initialize the
columns at design-time.

So after your control is constructed in the InitializeComponent method,
columns are added again by the designer-generated code.

You can try to prevent the addition of the columns when the control is being
hosted in a designer. In order to do this you need to check the
Site.DesignMode property of the Control, however Site will always be null in
the constructor. So you'll have to move the column-addition code into
another method that can evaluate Site.DesignMode:

protected override void OnControlAdded(ControlEventArgs e)
{
if (Site == null || !Site.DesignMode)
{
// add columns
}

base.OnControlAdded(e);
}

Realize that the columns shouldn't appear when the control is added to a
Form's designer.

Let us know if that works for you :)

--
Dave Sexton

"Rimpinths" <mi**********@yahoo.comwrote in message
news:11*********************@m7g2000cwm.googlegrou ps.com...
Following up on my own message...

I thought that I could at least work around this problem by calling
Columns.Clear() in my AddColumns() method, but even though the column
count is zero after I clear them, two sets of the columns still show
up. So where is the other set being stored?

I think I'm missing some fundamental understanding about user controls.
Is the moral of the story to just not do anything in the constructor?

Nov 22 '06 #3
Thanks for your detailed response, Dave.

Yeah, I guess it does make sense that the constructor gets called at
design time, and then of course at run time. What I don't get is why
the constructor gets called twice for the same instance of that
control? Why doesn't the design time instance get thrown out and a new
one created at run time? How can you even call a constructor twice for
the same instance?

Unfortunately, your suggestion doesn't work. Actually, it's worse -- it
ends up adding two columns at design time and another two columns at
run time. Go figure. And after adding it and removing it from my form
several times, the form designer code has references to a
dataGridViewTextBoxColumn5 and dataGridViewTextBoxColumn6, and so on.
Weird stuff. I'm still trying to get my head around the original
problem, not sure what's going on with your suggestion.

Here's another interesting test that I ran. I tried this bit of code in
the constructor...

Columns.Add("column1", "column1");
Rows.Add(3);
Rows[0].Cells[0].Value = "Hello,";
Rows[1].Cells["Column1"].Value = "world!";

try
{
Rows[2].Cells[1].Value = "I'm here.";
}
catch (ArgumentOutOfRangeException)
{
Rows[2].Cells[0].Value = "I can't find you";
}

Everything appears in the first column at design time and run time. And
the ArgumentOutOfRangeException is raised even at run time, despite the
fact that I can see that second column. How can you even access it?
Where does it exist?

I know that we developers are quick to attribute something to a bug
when it doesn't work the way that we expect it to, but this seems like
a genuine bug to me. I'm not sure if this is the general behavior of
constructors of user controls, or more likely, a particular problem
with the DataGridView control which I think went through a lot of
changes in .NET 2.0. I'm trying the think of similar scenario with
another control, but I can't. Oh wait, let me try deriving a ComboBox
and call this in the constructor...

Items.Add("Item #1");
Items.Add("Item #2");

Yep, same problem. Four items are listed, but Items.Count returns 2.
What the...? This seems like such a basic problem if I ran into this
after two days of playing around with derived controls. There must be
some way around it, perhaps something that I may not be doing right
after all, but I still think the proper behavior would be to throw out
the design time instance and start over at run time.

I'll probably just go back to calling a separate AddColumns method at
runtime as I said in my original message. It's just not worth the
effort to try figure out what's going on here, although I'm still
curious, just for the sake of my C# knowledge.

Thanks again for your help, much appreciated.

Nov 22 '06 #4
Thanks for your detailed response, Dave.

Yeah, I guess it does make sense that the constructor gets called at
design time, and then of course at run time.

One other thing that I noticed is that Visual Studio adds
this.dataGridViewDerived1.Columns.AddRange to the design code of the
form that the control gets added to. Anything you do in the constructor
of the user control gets added to the design code of the control's
container. I'm not sure if I understand the logic behind that behavior.
And one solution I did find is to remove the Columns.AddRange in the
design code after I add the control to a form, but what a hassle, why
should I need to do that?

Unfortunately, your suggestion doesn't work. Actually, it's worse -- it
ends up adding two columns at design time and then another two columns
at run time. Go figure. And after adding it and removing it from my
form several times, the form designer code has references to a
dataGridViewTextBoxColumn5 and dataGridViewTextBoxColumn6, and so on.
Weird stuff. I'm still trying to get my head around the original
problem, not sure what's going on with your suggestion.

Here's another interesting test that I ran. I tried this bit of code in
the constructor...

Columns.Add("column1", "column1");
Rows.Add(3);
Rows[0].Cells[0].Value = "Hello,";
Rows[1].Cells["Column1"].Value = "world!";

try
{
Rows[2].Cells[1].Value = "I'm here.";
}

catch (ArgumentOutOfRangeException)
{
Rows[2].Cells[0].Value = "I can't find you";
}

How can I even access that other column that I can see? Who does it
even belong to? I'm trying the think of similar scenario with another
control, but I can't. Oh wait, let me try deriving a ComboBox and call
this in the constructor...

Items.Add("Item #1");
Items.Add("Item #2");

Yep, same problem. Four items are listed, but Items.Count returns 2.
What the...? This seems like such a basic problem if I ran into this
after two days of playing around with derived controls. There must be
some way around it, perhaps something that I may not be doing right
after all, but I still think the proper behavior would be to throw out
the design time instance and start over at run time.

I'll probably just go back to calling a separate AddColumns method at
runtime as I said in my original message. It's just not worth the
effort to try figure out what's going on here, although I'm still
curious, just for the sake of my C# knowledge.

Thanks again for your help, much appreciated.

Mike

Nov 22 '06 #5
Hi,

I'm so sorry - I accidentally gave you code overloading the wrong method :|

The following code works just fine:

public class CustomDataGridView : DataGridView
{
protected override void OnCreateControl()
{
if (Site == null || !Site.DesignMode)
{
Columns.Add("Test 1", "Header 1");
Columns.Add("Test 2", "Header 2");
}

base.OnCreateControl();
}
}
Yeah, I guess it does make sense that the constructor gets called at
design time, and then of course at run time. What I don't get is why
the constructor gets called twice for the same instance of that
control? Why doesn't the design time instance get thrown out and a new
one created at run time? How can you even call a constructor twice for
the same instance?
You can't call a constructor twice on the same instance. The runtime
instance and design-time instance are not the same. When you start your
application, even with a debugger attached, it will run in its own process.

<snip - sorry - >

--
Dave Sexton
Nov 22 '06 #6
Hi,
One other thing that I noticed is that Visual Studio adds
this.dataGridViewDerived1.Columns.AddRange to the design code of the
form that the control gets added to. Anything you do in the constructor
of the user control gets added to the design code of the control's
container. I'm not sure if I understand the logic behind that behavior.
Not just any code is generated.

You've got to realize that the columns may be designed themselves. In other
words, you can add and remove columns from within an editor dialog, which
means that the designer has to serialize code to persist the changes that
you make. Guess where that code is persisted?

The point of a designer is to design the Form for use at runtime, so if you
add a DataGridView with some predefined columns, the designer is going to
see those columns and serialize code to generate them at runtime. It
doesn't know that you've added them yourself in the constructor and not
after the control was added to the Form.
And one solution I did find is to remove the Columns.AddRange in the
design code after I add the control to a form, but what a hassle, why
should I need to do that?
The designer code is "live" code that will be modified by VS on-the-fly.
Changes like that will probably not last for very long. Try closing the
Form designer and reopening it or try closing VS and reopening it and you'll
probably see what I mean.

<snip - still sorry you wasted your time writing this - :) >

--
Dave Sexton
Nov 22 '06 #7

Rimpinths wrote:
One other thing that I noticed is that Visual Studio adds
this.dataGridViewDerived1.Columns.AddRange to the design code of the
form that the control gets added to. Anything you do in the constructor
of the user control gets added to the design code of the control's
container. I'm not sure if I understand the logic behind that behavior.
And one solution I did find is to remove the Columns.AddRange in the
design code after I add the control to a form, but what a hassle, why
should I need to do that?
What is happening here is that the Columns property of the original
data grid contains code that tells the Designer what the default
columns look like. In fact, every property of a control has some code
that tells the Designer what the default is for that property.

When it comes time to serialize the control into code, the Designer
serializes code for all properties that have values _different from
their default values_. It doesn't know that the property's value was
changed in the constructor rather than by the programmer in the
Designer itself. All it knows is that property X has a value other than
default, so it serializes that value.

You need to read up on "design time" logic in Visual Studio and how to
design your own controls. There are several MSDN articles out there,
lots of documentation, and several newsgroups. Some things are very
easy to do, while others are difficult. I still haven't gotten the hang
of compound properties like Columns, but others have, so it can be done
properly.
Unfortunately, your suggestion doesn't work. Actually, it's worse -- it
ends up adding two columns at design time and then another two columns
at run time.
No... it doesn't add "two columns at design time and then another two
columns at run time." What it does is add two columns in the
constructor, and then add them again in the serialized code for the
control. Again, the Designer didn't know that those two columns were
added in the constructor, because you don't have any attributes /
methods to indicate that those two columns are defaults and shouldn't
be serialized.

In fact, this points out why having some "default columns" may be a
problem: if you then add a third column at design time the Designer
will see that the value of Columns is not the default of two columns
and will then serialize code to add all three columns, resulting in
five. You may have to write your own serialization code in order to get
this to work, and IMHO that's pretty advanced design-time stuff.
Probably not worth the hassle, although I'm told that it can be done.
Go figure. And after adding it and removing it from my
form several times, the form designer code has references to a
dataGridViewTextBoxColumn5 and dataGridViewTextBoxColumn6, and so on.
The names for the columns seem to be monotonically incrementing from
the highest name used so far. The Designer doesn't appear to try
reusing column names that are free. Not sure why, but there you go.
Here's another interesting test that I ran. I tried this bit of code in
the constructor...

Columns.Add("column1", "column1");
Rows.Add(3);
Rows[0].Cells[0].Value = "Hello,";
Rows[1].Cells["Column1"].Value = "world!";

try
{
Rows[2].Cells[1].Value = "I'm here.";
}

catch (ArgumentOutOfRangeException)
{
Rows[2].Cells[0].Value = "I can't find you";
}

How can I even access that other column that I can see? Who does it
even belong to?
Since you don't say what the result of running the code is, I'm not
sure what the issue is here....
I'm trying the think of similar scenario with another
control, but I can't. Oh wait, let me try deriving a ComboBox and call
this in the constructor...

Items.Add("Item #1");
Items.Add("Item #2");

Yep, same problem. Four items are listed, but Items.Count returns 2.
What do you mean, "Four items are listed"? Listed how? How do you know
that there are supposedly four? Where are you checking Items.Count?

Nov 22 '06 #8
The following code works just fine:

Yes, it does, thank you!
You can't call a constructor twice on the same instance. The runtime
instance and design-time instance are not the same. When you start your
application, even with a debugger attached, it will run in its own process.
Yeah, I realized that didn't make any sense about two minutes after I
posted that message and consequently deleted it. I considered that I
never run it as an exe, and then I saw that it did the same thing when
I did, which blows the design time and runtime instance out of
competition.

Mike

Nov 23 '06 #9
The point of a designer is to design the Form for use at runtime, so if you
add a DataGridView with some predefined columns, the designer is going to
see those columns and serialize code to generate them at runtime. It
doesn't know that you've added them yourself in the constructor and not
after the control was added to the Form.
Okay, I think that nails the concept that I was missing: serialization.
This is what I need to learn more about.
<snip - still sorry you wasted your time writing this - :) >
No, not a waste of time at all. I'm so grateful that people like you
are willing to spend time answering questions from strangers.

I could've worked around this problem in no time, but I knew that I
should probably try to figure out what was going on, it could help my
C# knowledge, and it has.

Mike

Nov 23 '06 #10
When it comes time to serialize the control into code, the Designer
serializes code for all properties that have values _different from
their default values_. It doesn't know that the property's value was
changed in the constructor rather than by the programmer in the
Designer itself. All it knows is that property X has a value other than
default, so it serializes that value.
Here we go again, serialization, this is what I need to learn more
about.
You need to read up on "design time" logic in Visual Studio and how to
design your own controls. There are several MSDN articles out there,
lots of documentation, and several newsgroups. Some things are very
easy to do, while others are difficult.
Yeah, I'm figuring that out. I was actually just wanted to create a few
controls that I could reuse within a project, to standardized fonts and
colors and such. If I wanted to sell it or use it throughout several
projects, I'd problem spend more time understanding the problem.

I think that this is probaby an aytpical case. The core problem is
adding objects to a collection (columns to a grid, items to a combobox)
in the constructor. In most cases, performing tasks in the constructor,
like setting properties and such, this wouldn't be a problem.
Since you don't say what the result of running the code is, I'm not
sure what the issue is here....
What, you can't do it in your head?

Sorry, the resulting code is that two columns are displayed with the
headers "column1", and everything gets written to the first column. If
I try to access the second columns with Rows[2].Cells[1], it throws
ArgumentOutOfRangeException. If I ask how many columns are there, it
returns Columns.Count = 1. You can't access that second column in any
way that I can figure out, so where does that object exist?
>
I'm trying the think of similar scenario with another
control, but I can't. Oh wait, let me try deriving a ComboBox and call
this in the constructor...

Items.Add("Item #1");
Items.Add("Item #2");

Yep, same problem. Four items are listed, but Items.Count returns 2.

What do you mean, "Four items are listed"? Listed how? How do you know
that there are supposedly four? Where are you checking Items.Count?
I'm checking Items.Count at runtime, after the constructor has been
called. The ComboBox displays 4 choices in the drop down menu, but
Items.Count says that there are only 2 items. It's a similar problem as
the one with the columns above.

Mike

Nov 23 '06 #11
Hi Mike,

<snip>
I could've worked around this problem in no time, but I knew that I
should probably try to figure out what was going on, it could help my
C# knowledge, and it has.
I admire your desire to learn instead of just copying :)

--
Dave Sexton
Nov 26 '06 #12

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
3674
by: Robert | last post by:
Hello all... In my code below, the Notify Constructor and Destructor is getting called twice and it appears that a new Notify object is created on the 2nd call. The 2nd call is caused by this...
3
3898
by: Alexander Stippler | last post by:
Given the following code snippet we get some unexpected behaviour: //-------------------------------------------------------------------- #include <iostream> using namespace std; struct A {...
10
1764
by: johndoe | last post by:
While creating a shopping cart application I noticed a strange bug which resulted in the Constructor and everything being called twice. I was using Inherited classes ClassShowProducts inherited...
8
2251
by: David Lozzi | last post by:
Howdy, I have a user control that is a report to display data. On the page the control is inserted in, I have filter options to filter the report. When I try to do something like this, nothing...
0
3915
by: tony | last post by:
Hello! This is a rather long mail but it's a very interesting one. I hope you read it. I have tried several times to get an answer to this mail but I have not get any answer saying something...
0
1413
by: Kristian Frost | last post by:
Hi, I'm just getting started with VB.Net, and I'm having trouble getting the routing around of some of the data straight in my mind, which has led me to the following problem. Basically, I'm...
10
2947
by: Szabolcs Horvát | last post by:
Consider the attached example program: an object of type 'A' is inserted into a 'map<int, Am;'. Why does 'm;' call the copy constructor of 'A' twice in addition to a constructor call? The...
20
432
by: royashish | last post by:
Hi all , A question to the C++ fraternity , Why the Copy constructor ? Was suggested in Effective C++ , In Case the Object contains a Char* , a assignment operator = makes the two object...
6
7228
by: =?Utf-8?B?TWF0dA==?= | last post by:
I'm having a problem with a static class constructor being called twice. I have the static class MasterTaskList which uses a BackgroundWorker to execute multiple methods on a separate thread. The...
0
7223
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
7115
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
7321
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
7377
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
7489
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
3191
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
3179
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
762
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
414
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.