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.