Designers and Abstract Classes

Brian in Coding | 12 Comments 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.

Comments (12) -

Ian Griffiths, Tuesday, June 1, 2004 at 5:29 AM

Couldn't you use a mixture of techniques:  attempt to provide a stubbed concrete class at design-time by default, but define a custom attribute that enables developers to specify their own concrete design-time class.
That way, extra developer effort is only required in those cases where it would have been inevitable.

Frank Hileman, Tuesday, June 1, 2004 at 9:45 AM

Everyone runs into this "problem" at one point, but once they realize the rationale, no one complains any longer. This is because the simplest solution, using virtual instead of abstract methods, has no real drawbacks in practice. The only thing you lose is the "reminder" to implement an abstract method. Non-abstract classes are actually more usable, since you can override only the members that are important to you.
An "reminder" attribute might also be effective -- an attribute that emits a warning if a member is not overridden, in a derived class that is not abstract, or is not decorated with another attribute specifically designed to suppress the warning ("designer abstract").

Jake Kirk, Friday, July 23, 2004 at 8:58 AM

I couldn't get this to work to my satisfaction using virtual and override...
The Visual Studio Form Designer should probably respect "virtual" and "override" keywords on base and derived classes but doesn't.
So the Form Designer has this "feature" where it doesn't recognize the override keyword and instead calls into the base class.
e.g.
// base class for all Forms used (can't be abstract or interface).
public class HCWForm : System.Windows.Forms.Form
{
  /// <summary>
  /// Return a reference to the current panel
  /// </summary>
  public virtual Panel panel
  {
    get  
    {
    // can't be abstract as DevStudio will be unable to load Form Designer,
    // so we can't require implementation, but how about enforcing an override?
    throw new Exception( "member function must be overridden!");
   }    
  }
}

Joey Calisay, Tuesday, August 24, 2004 at 3:25 AM

The line "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" boggles me.  How does VS know the settings in the InitializeComponent if it never executes these lines at design time.  Does anyone know how this interpretation is done?

Brian Pepin, Thursday, August 26, 2004 at 8:51 AM

Joey --  VS has an interpreter that interprets the lines within InitializeComponent.  After it creates the base class it then parses the contents of InitializeComponent and interprets the statements in there.  The interpretation is handled by a parser (which VS provides) that converts the statements into a CodeDOM tree, and a set of serializer objects that interpret the CodeDOM tree.

Jeff Berkowitz, Wednesday, September 29, 2004 at 5:56 PM

Thanks so much for this posting!  I never completely understood the issue with the designer not respecting overrides although I'd seen its consequences.
For the record, I think you can do stuff like this:
public virtual IDictionary DialogState // "abstract" property in visual base class
{
  get
  {
    if (Site == null || Site.DesignMode == false) // Runtime
      throw new NotImplementedException("Subclass should have overridden IDialogInfo.DialogState");
    return null; // design time
  }
}

David Brunning, Wednesday, November 10, 2004 at 5:06 AM

I prefer the idea that VS.Net tries to build a concrete class from the derived form when I open it in the designer.  I would much rather put in the effort and make sure that all my abstract methods were implemented before I could design the form than have to find another way of working.
Someone posted that virtual methods are just as flexible but the problem is that I need to write code which enforces standards for my product - no matter what you say people (like me) are lazy and unless they are forced down the right road (as far as possible) they will probably stop short.

Jens Samson, Friday, February 4, 2005 at 1:19 AM

On my quets to get Visual Inheritance to work with Abstract Base Classes, I came across your article that explained why it doesn't work.
While it was interesting, I found the last lines : '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.' really the most interesting.
Are you stil planning on writing about that ?  I googled the net for 'custom type description providers' but all I got back was a link to this article.  I'd be very much interested in a solution for this problem, even if I have to do it myself.

Brian Pepin, Saturday, February 5, 2005 at 10:49 AM

Jens -- Yes, this is still on my very long list of things to do, but I haven't had time to write it yet.  

Bryce Covert, Saturday, March 12, 2005 at 12:25 PM

I think the best way of solving this is use an internet solution that I found.
Currently (as far as I know) there's no compiler directive variable for whether it's the designer. Before creating objects for the designer, it would set this directive (something like WINFORMS_DESIGNER). Then, if it encounters an abstract class, it would just overrwrite the text (temporarily) to something like:
#If WINFORMS_DESIGNER Then
Public Class BaseClass
#Else
Public MustInherit Class BaseClass
#End If
    Inherits System.Windows.Forms.Form
And do the same for the abstract functions.
That way it's very little changed to the architecture of the WinForms designer, and fixes the issue.

Nathan Alden, Sunday, April 3, 2005 at 8:49 PM

Ok, so we all know that designing an abstract form directly works. The designer simply creates an instance of the base class. And we all know that at runtime, .NET has no trouble creating the form that is derived from an abstract form. My solution:
If the designer is designing an abstract form, find the closest form in the inheritance chain that is not abstract and design it.
If the designer is designing a non-abstract form, no matter what its base class is, design the form itself rather than the base class.
Just be sure that the DesignMode property has been initialized in the Form class' constructor so that I may skip any runtime-only initialization code in my derived form's constructor by checking "if (!DesignMode)".

Markus, Monday, May 2, 2005 at 2:50 AM

I don't know all about the inner workings of VS, but in my opinion the arguments given for not implementing the stand-in class solution are rather weak.
Argument 1: "It places a burdon on the app developer". I am convinced that all developers who have experienced the problem and found your article in their search for a solution are ready to implement stubs to get rid of the the burdon of not being able to use the designer when implementing abstract controls (the same problem does apply to all controls, not just forms, right?). Besides, if they weren't, the designer would just behave the way it does today so it wouldn't really be a burdon but an option.
Argument 2: "once we created an instance of the concrete class, we would use that for all serialization and property browsing".
Why would you do that? Woud it be unthinkable to in the designer keep track of which members were implemented by the stand-in class?
Comments are closed