How does the Windows Forms designer in Visual Studio load a Form?
Here are a couple of questions we often get from developers who are new to Windows Forms:
1) The designer allows me to open an abstract Form, but not a Form that inherits from an abstract Form. Why is that?
2) I got a load error when I tried to open my Form in the VS designer. So I attached a debugger to VisualStudio (devenv.exe), set a breakpoint in my Form's InitializeComponent to step through it to see what the problem is. However, the breakpoint is not getting hit.
The answer to these questions lies in understanding how the designer actually loads a form at "design-time". When you open a new Windows Application project in VS, you see an empty form called Form1 in design view. Now, you haven't built the project yet, so how is the designer able to create an instance of Form1 and show it? Well, the designer is not really instantiating Form1 at all. It is creating an instance of the base class of Form1, i.e., System.Windows.Forms.Form. With a basic knowledge of object oriented programming, you will find that this intuitively makes sense. When you are designing Form1, you start with the base class, Form, and customize it. This is exactly what the designer helps you to do.
Now let's say you added a bunch of controls to the Form and closed the designer. When you reopen the designer, the controls are still there. However, the base class Form doesn't have these controls on it, so if the designer isn't running the constructor of Form1, how did it show the controls? The designer does this by deserializing the code in InitializeComponent. Each language that the designer supports has a CodeDomProvider that is responsible for providing a parser that parses the code in InitializeComponent and creates a CodeDom representation of it. The designer then invokes a set of CodeDomSerializers to deserialize this into actual Controls (or more broadly, Components) that it can add to the design time Form. Now, I have glossed over a lot of details in that description, but the point here is that Form1's constructor and InitializeComponent are never really invoked. Instead, the designer parses the statements in InitializeComponent to figure out what controls to instantiate and add to the form.
Armed with this knowledge, it should be easy to also understand why:
1) Form1 must be built before you can add another Form, say Form2, that visually inherits from it. This is because the designer for Form2 has to instantiate Form1, not System.Windows.Forms.Form. This also explains why if you open Form2 in the designer, attach a debugger to Visual Studio and set a breakpoint in Form1's InitializeComponent, the breakpoint does get hit.
2) There is a comment above InitializeComponent that warns you against modifying it manually. This is because the designer needs to parse this code, and it has some limitations as to what it can parse. It is generally guaranteed to parse whatever it serialized in there, but not arbitrary code that you may add.
3) If you are manually (through code) adding a control to the form in the constructor or in the Load event handler, the control doesn't show up in the designer. This is because the designer doesn't parse that - it only parses InitializeComponent.
Comments
- Anonymous
December 12, 2004
Although I understand the way the Windows Form designer works, I still see it as a very big limitation that it cannot show an 'abstract' form.
Why can't the designer just try to inherit from the abstract form, or, if the abstract form is in the same project (which it most likely is), try to parse the InitializeComponent code from the abstract from and show it that way?
It doesn't seem like an impossible thing to do and if it fails, well, then you just show us a nice error message and everybody's happy, right? ;)
The way it works now, people are forced to make classes that should be abstract not abstract because of these limitations. This leads to more buggy code because it's easier to loose track of what methods should be override and which ones shouldn't, it's easier to by mistake initialize the form that should be abstract, what could lead to inpredictable results as well :). - Anonymous
December 12, 2004
Um... well I can prove that this isn't actually true. The constructor () gets called for sure.
Put a connection to a database on a form, put a connection.open() in the constructor after the InitializeComponent.
Change the connection string to a database that doesn't exist.
save, compile and re-open the form in the designer. You'll see (after a long pause while it waits for a time out on the database server not being there) that in your task list an error about the database server not existing or access denied.
Thus, the constructor is absolutely run. Just like it's run when instantiating the controls in the designer for each control on the form.
And of course the .DesignMode property doesn't work correctly with inheritied stuff most of the time, so you can't use it to exit your constructor correctly and consistantly so that the con.Open() doesn't get called, so you have to do something else special that would only be set at runtime as a check.
Stupid waste of code and time. - Anonymous
December 12, 2004
Frederik: The designer does allow you to design an abstract form, but not one that inherits from it. But I think that's what you meant.
The problem with trying to inherit from an abstract form is that we wouldn't know how to implement any abstract methods it may have, which may cause the form to be displayed incorrectly, if those methods get called at design time.
Check out Brian's excellent post at http://www.urbanpotato.net/Default.aspx/document/1772 for more on this very topic. - Anonymous
December 12, 2004
James - are you sure it is the call in the constructor that is causing the issue? What if you put a MessageBox.Show in there? What if you add a control to the form? - Anonymous
December 12, 2004
This is even the reason's why an error in the Dispose Method of the form can crash the IDE when closing the designer!!! - Anonymous
December 13, 2004
Indeed, I was talking about designing a form that inherits an abstract form.
Yes, I can see that just blind inheriting the abstract form doesn't work.
However, what's wrong with the parsing the InitializeComponent() method?
This would display the base (abstract) form as it was seen by the base form designer. It would be quite useful for me, I think :) - Anonymous
December 13, 2004
Parsing InitializeComponent() of the base form would be different from running the constructor, since the rest of the code in the constructor will be ignored. This would be different semantics than when the base form isn't abstract, so could be a source of confusion. - Anonymous
December 27, 2004
I'm sure that the constructor gets called (I think it filters some things out) because I see the errors thrown because the connection fails with a bad connection string because it's built when the app runs from it's config file, in the tasks list when the form is opened. Further, if I don't exit the contstructor with a return; before the con.open() the form takes exactly 15 seconds longer to open than if I do. Not conincidently, my timeout on the connection is 15 seconds.
Thus the constructor is processed (at least some of it) - Anonymous
December 30, 2004
The comment has been removed - Anonymous
January 02, 2005
The comment has been removed - Anonymous
September 14, 2005
First a warning that doing what I describe could lead to the designer working even worse. In my case Visual Studio stopped recognizing my inherited forms as forms at all, but this worked for adding new inherited forms via the wizard. This was all done in Visual Studio .NET 2003.
I've found that it's possible to view a form that's inherited from an abstract form in the designer by using preprocessor directives. First, create a new configuration. Then add a “Conditional Compilation Constant” in the project settings, I’ve named mine “DESIGN”. Then in the abstract form provide two class declarations, for example:
#if DESIGN
public class BaseForm : System.Windows.Forms.Form
#else
public abstract class BaseForm : System.Windows.Forms.Form
#endif
Also add a default constructor if you don’t have one already, use #if if you don’t want the default constructor exposed to anything other than the designer.
#if DESIGN
public BaseForm()
{ InitializeComponent(); }
#endif
And that should do it. Anybody else have luck trying this? - Anonymous
October 09, 2005
Very interesting article (and discussions).
I'm having a problem creating a form that inherits from another. The base form is in the application, and I would like the derived form to also be in the application.
But VS2005 is giving me an error message saying essentially that the base form needs to be in a separate assembly. Is this because of how the designer works, as you described?
Is it possible to have a base form and a derived form in the same EXE application, i.e., without moving the base form out into a separate assembly? - Anonymous
October 17, 2005
tzagotta: This should work fine. Which build of VS2005 are you using? - Anonymous
November 16, 2005
After working perfectly the Form Designer in VS Beta 2005 has suddendly stopped working.
When I open up a project and double click on the frmMain.cs in the Solution Explorer instead of the usual emulation of the device screen I get a blank page with just the control icons across the screen.
Can anyone tell me what is going wrong here? (I should add that the EXE works perfectly when installed on a device...however, its rather difficult to redesign any parts of the forms without the designer working!)
Thanks
Will Chapman - Anonymous
November 17, 2005
I suggest reporting a bug through MSDN Product Feedback (http://lab.msdn.microsoft.com/productfeedback/). - Anonymous
June 16, 2006
You can also do a if (Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName).ToLower() == "devenv.exe") in the constructor to see if the designer is trying to instantiate the class. Wrap that in a #if to make sure it is not compiled into production builds. The if statement can protect statements that don't work so well when in design mode. We have some base classes that have code in the constructor that kills the designer. - Anonymous
August 16, 2006
Can confirm all of that after two days (fulltime an past-working hours) that everything is true.
I can add, for .NET Studio 2003, that the designer seems to parse the code, especially the inherit statement.
I had two lines in a VB- Class:
Class SomeImplementation
Inherits Form
Inherits PageForm
(the latter class deriving from Form).
This did not compile, of course. But: When I swap those two lines, the designer reports an error. When I don't it happily loads the form.
More supprisingly: If I had the source of the PageForm in the same project, the designer did load the SomeImplementation form.
Regards,
Jens - Anonymous
November 10, 2006
The comment has been removed - Anonymous
June 09, 2009
PingBack from http://insomniacuresite.info/story.php?id=4208