Brian in
Coding |
May 31, 2004
Imagine this: you have a great software project you’re working on. You’ve decided to use Visual Studio’s Visual Inheritance feature so you can re-use portions of your application’s UI. While working on the base classes, you decide that all base forms need to provide a “Title” property, so you add the property and make it abstract. You go on for a little while longer and then open a designer for a form that derives from your base form. What do you get? A nice fat juicy error message stating that abstract classes cannot be designed.
The Visual Studio designer cannot design abstract base classes. Did Microsoft completely forget about object oriented design methodologies? Is this a ploy to get you to upgrade to a newer version of Visual Studio? No, this is actually just a side-effect that falls out from how the Visual Studio designer works.
The Visual Studio designer works by actually creating instances of objects on your form. If you have a form with two buttons and a text box, for example, the form designer creates two real buttons and a real text box. The one thing the designer cannot create, however, is an instance of your form. Why? Because you’re currently designing it, and it isn’t compiled. Instead, the form designer creates an instance of your form’s base class. If you’re just inheriting from Form, that’s what gets created. If you’re inheriting from “MyBaseForm”, it creates an instance of that. Once the base form instance is created, the form designer interprets all the code within the InitializeComponent method to create the other controls on the form.
Because the form designer tries to create an instance of the base class, it fails if the class is abstract. There are a variety of things Microsoft could do to fix this problem. Why haven’t we? Believe me, we’ve thought about it, but none of the solutions we’ve come up with offer a very seamless experience for end developers. Here are some of our ideas:
- Just Walk Base Classes. In this model, if the class was abstract we would simply walk up to higher level base classes until we found a non-abstract class. This allows you to “design” abstract classes with no additional effort on your part. The biggest problem with this model is that it is likely that there were properties and behavior you really wanted on your base class, and you probably want those to transfer to the designer as well. This becomes more pronounced if your base class has other controls, so you’re using Visual Inheritance. If we walked up and created an instance of “Form” instead of your own class, all your controls would disappear in the designer.
- Require the User to Provide a Stand In Class. In this model, you would be responsible for creating a concrete design time version of your abstract class. You’d use some sort of custom attribute on your abstract base class that relates your abstract class to a concrete class we’d create at design time. There are two issues with this model I find undesirable. First, it puts the burden on application developers to create these design time classes for all their abstract classes. Second, once we created an instance of the concrete class, we would use that for all serialization and property browsing. If an application developer added additional properties to the concrete class they would be reflected in the property window and may even be written to code, which would cause a compile error.
- Allow a Designer to Create its Component. In the normal behavior of the designer, a component or control is created first, and then its design time object is created and initialized. This method would reverse the process; we’d find a designer first and initialize it, and then ask the designer to create an instance of the component it is designing. This way, the designer could create a stand-in concrete version of a class. This is undesirable for the same reasons as above: it puts too much burden on the application developer.
- Provide Stub Implementations of Abstract Members. In this model we would create a stand-in concrete class automatically by implementing stubbed methods for all abstract members. This has the advantage of keeping the application developer’s experience clean; you don’t have to know anything about the mechanics of what’s going on. It has a serious disadvantage, however: it cannot be done for wide variety of base classes. What happens if part of the API of an abstract class needs to be invoked during construction and needs to return a value? There is simply no way to create any method that returns real values, so we would fail in a broad set of cases anyway.
Well, that pretty much sums up where we are. As none of these ideas is really that stellar, we haven’t implemented any of them. Does anyone have an opinion? Requiring the application developer to provide a stand-in class for abstract classes is my personal favorite, but it does place quite a bit of burden on application developers.
Interestingly, while we don’t have any plans to offer this feature in our upcoming Whidbey release, it is actually possible to implement it yourself using a feature called custom type description providers. I’ll publish something on that soon.
windows forms component model