Click here to Skip to main content
Click here to Skip to main content

Inheriting a Form from an Abstract Class (and Making it Work in the Designer)

By , 20 Jan 2008
 

Introduction

For those who work with .NET inheritance, it is quite common to write abstract classes in order to store portions of code which will be shared by sibling subclasses and therefore enforce the code reuse. Obviously, sibling subclasses may include System.Windows.Forms.Form classes; for example, an application using an MVC pattern may have forms inheriting from an abstract class implementing a View. Most commonly, base abstract classes for forms can be useful to share common behaviours between forms (a title, a set of buttons or methods and so on).

Problems appear when you try to edit a Form inherited from an abstract class with Visual Studio Designer: an error message fires, stating that "The designer must create an instance but it cannot because the type is declared as abstract". The same error is fired by SharpDevelop, revealing that this is not just a Visual Studio bug but, probably, a Framework architectural issue.

AbstractForms

Delphi's and Borland Builder C++'s designers perfectly handle abstract based forms but .NET doesn't. Moreover, Microsoft doesn't plan to solve the inability to cope with abstract forms in upcoming Visual Studio versions.

To solve this problem, a few workarounds can be used: in this article I will introduce the one I consider to be the most comfortable to use, but which is neither the best nor the most elegant one.

The trick consists in inheriting the form from an abstract class only in release builds and in writing a suitable concrete version of it which can be used only at design time and in debug builds.

Background

Understanding the reason for this behaviour may not be strictly necessary but it could be nevertheless interesting.

We could believe that, in order to edit a Form, the Designer just needs to directly instantiate it. Far from it, the error message clearly states that the Designer needs to instantiate the base class rather than the derived one but, being the first one abstract, no instance of it can be created.

As a matter of fact, when the Designer handles with Forms, it first uses Reflection to create an instance of the base class (System.Windows.Forms.Form), then it parses the InitalizeComponent method of the derived class and it finally applies all the derived class's properties on the base class's instance, hence allowing the user to modify the derived class. In short, the user always works with a base class modified on-the-fly.

In his excellent article Brian Pepin lists some of the fixes Microsoft took into consideration and explains in details why none of them have been adopted.

Approaches

Some articles propose to change the OO architecture in order to prevent the problem from appearing.

The widespread solution is to avoid the use of abstract methods (which would tag the entire base class as abstract) and use instead empty virtual methods. Doing that, the class behaves like abstract, but works in the Designer too. The main problem with this approach is related to the differences between virtual and abstract methods.

Virtual methods allow subclasses to override the base method (eventually, empty) with their own implementation, while abstract ones, which are implicitly virtual, strictly require the class to provide an implementation using the same signature. Using virtual methods may lead the developer to incidentally forget to provide an implementation: in this case, no alert by the compiler would be fired. A further suggestion could be, then, to always write virtual methods throwing a NotImplementedException to remind the developer to override the method. It would happen only at run-time and you may agree that a solution enforced by the compiler would be much more comfortable.

It's not generally a good idea to change the architecture in order to just get around a problem: an object-oriented hierarchy is supposed to have been chosen with a strong, clear and correct design architecture in mind and an approach which can preserve the exact hierarchy is generally a better approach.

Brian Pepin (again) suggests the best solution which both preserves the exact OO design and completely solves the problem. Unfortunately, Brian's approach only works with Visual Studio Whidbey (2005) and, even if it's almost perfect and really elegant, it's considerably less immediate than the following solution. Let me stress again that Brian's solution is the real good one. The one described here should be considered just an enhanced version of the virtual methods solution, with the only advantage of being very simple and of working on every Visual Studio version.

The Solution

The weakness of the virtual methods solution is the lack of compiler's alerts in the case of a missing method implementation. As stated in the introduction, you may provide two classes, one abstract and the other one concrete, to be used respectively for build and for debug releases. Instead of writing two separate files, it's easier to use an #if compiler directive which switches between the two class definitions:

namespace CodeProject.AbstractForms
{
    #if DEBUG
        public class AbstractForm : Form
        {
    #else
        public abstract class AbstractForm: Form
        {
    #endif
        
        #if DEBUG
            public virtual void MyMethod()
            {
                throw new NotImplementedException();
            }
        #else
            public abstract void MyMethod();
        #endif
        
        }
}

You can switch between Debug and Build releases in Visual Studio with a bunch of clicks (few more with SharpDevelop), but you should remember to recompile your project and close and reopen your form in order to inform the Designer about the changes.

With this simple trick, a release build will throw a compiling error for a missing overridden method, while in debug mode every form will be perfectly editable.

Implementing the Approach with SharpDevelop

Visual Studio is smart enough to correctly deal with curly brackets, ignoring open parenthesis in not enabled portions of code; SharpDevelop is less efficient and throws errors declaring not closed brackets.

Since I'm a big SharpDevelop's fan, I found it very frustrating. The only workaround I managed to obtain for SharpDevelop is not that elegant, but it's perfectly working. The trick is avoiding curly brackets to appear inside a #if statement, like in the next sample code:

namespace CodeProject.AbstractForms
{
    public partial 
    #if RELEASE
        abstract
    #endif
    class AbstractForm : Form
    {
        public AbstractForm()
        {
            InitializeComponent();
        }

        public
            #if DEBUG
            virtual 
            
            void MyMethod()
            {
                throw new NotImplementedException();
            }
            #else
            abstract void MyMethod();
            #endif
    }
}

Using the Code

In the sample code, the form DerivedForm inherits from AbstractForm, which is concrete in debug builds and abstract in release ones. AbstractForm defines respectively MyMethod as a virtual method (throwing an NotImplementedException) or an abstract method.

You can comment the #define directive in the first line of the sample code in order to disable the implementation of MyMethod().

#define IMPLEMENTMYMETHOD

#if IMPLEMENTMYMETHOD
        public  override void MyMethod()
        {
            //Do something
            MessageBox.Show("MyMethod called");
        }
#endif

You can verify that in debug mode, both DerivedForm and AbstractCode can be successfully modified in Visual Studio Form Designer. As explained, if MyMethod() is not implemented in the derived class, a release build compilation fails.

In DerivedClass there's a little piece of code doing some reflection in order to find out which version of MyMethod() will be called:

private void CallMyMethod_Click(object sender, EventArgs e)
{
    Type type = this.GetType();
    MethodInfo[] methods = type.GetMethods();
 
    foreach (MethodInfo info in methods)
    {
        if (info.Name == "MyMethod")
        {
 
            if (info.DeclaringType.FullName == 
                    "CodeProject.AbstractForms.AbstractForm")
            {
                // MyMethod implemented only 
                // in the base class as virtual
            }
            else
            {
                // MyMethod implemented in the derived class.
            }
            info.Invoke(this, null);
            break;
        }
    }    
}

History

  • 2008.09.01 First version of the article
  • 2008.15.01 Fixed the wrong Whidbey version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Arialdo Martini
Software Developer
Italy Italy
Member
I'm a freelance software developer working on C#, PHP and CakePHP. At the moment I'm mainly working for an Italian software house on a quite big software based on .NET, PostGreSQL and NHibernate.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalform inheriting from abstract classesmemberme_pollack8 Jul '10 - 12:59 
my issue is that I inherit from an abstract class the solution is one o the following
 
Solution 1) Change comment to design
 
//public partial class TheForm: UserControl, IInterface
public partial class TheForm : AbstractClass
 
Solution 2) Define Design
 
//#define DESIGN
#if (DESIGN)
public partial class TheForm: UserControl, IInterface
#else
public partial class TheForm : AbstractClass
#endif
 
I have read that this was fixed in 2008. It was not, perhaps 2010.
GeneralA similar solutionmembersdstorm27 Feb '10 - 4:48 
I used the following solution in my code. It is basically the same idea, but it is more elegant in my opinion.
 
Here goes:
#if DEBUG
abstract class RealAbstractClass
#else
abstract class AbstractClass
#endif
{
    public abstract void AbstractStuff();
}
 
#if DEBUG
class AbstractClass : RealAbstractClass
{
    public abstract void AbstractStuff()
    {
        throw new NotImplementedException();
    }
}
#endif
 
The good thing is that the members of the dummy class can be automatically generated by Visual Studio. Smile | :)
 
I hope this helps someone...
GeneralBrian Pepin's OO solutionmemberserge1p16 Dec '08 - 19:12 
You refer to Brian's approach as the best solution as it works in VS05.
 
I have a problem understanding that approach (I use VS05), as it implies that the base class should declare the type of the inherited class. To me, if I have only one inherited class - there is not point to separate the code into base and implementation.
But I fail to undestand how to apply the proposed solution if you need to inherit the same base class in two different forms.
 
May be you can shed some light into it - I could not find a way to ask this question on the Brian's site - and I think it is relevant to this topic.
 
Thanks in advance - Serge
GeneralTwo thingsmemberTrendyTim14 Jan '08 - 12:37 
1) Whidby is 2005 and not 2008
 
2) That solution would appear to work fine, unless you actually want to run it in debug mode, and/or requires lots of build config switching between running and designing.
 

The best solution would be their automatic stubs and a fool proof way of detecting when code is being run from within the designer so certain method calls can be avoided if need be (though that detection code would be nice regardless).
GeneralRe: Two thingsmemberArialdo Martini14 Jan '08 - 23:23 
> 1) Whidby is 2005 and not 2008
 
Fixed. Thank you!
 
About automatic stub: this is a great suggestion! It may appear weird, but I didn't know about code snippets and it's a shame, because they are so useful (and, happily they works in SharpDevelop too). Now it's time to study them and write a snippet for this article. I hope I'll publish it soon. Thank you again for the suggestion.
 
>a fool proof way of detecting when code is being run from within the designer so certain method calls can be avoided if need be (though that detection code would be nice regardless).
 
It sounds interesting. I still have no idea about how to do it.
 
Thank you, man
 
--
Arialdo Martini

GeneralRe: Two thingsmemberTrendyTim15 Jan '08 - 1:12 
Arialdo Martini wrote:
About automatic stub: this is a great suggestion!

 
Im not sure if its possible to do it wodul need to be implemented at the IDE level, as I was talking about one of the considerations in Brian Pepin's article.
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.
 
Arialdo Martini wrote:
It sounds interesting. I still have no idea about how to do it.

 
Yeah its technically impossible for a fool proof, its something MS needs to address, unfortunately (because in typical fashion they probably never will).
 
Arialdo Martini wrote:
Thank you, man

your welcome, im not sure there was anything useful in what i said, it was just ramblings, but if you found something useful in there your a smarter man that i am Smile | :)
GeneralRe: Two thingsmemberZoltan Balazs15 Jan '08 - 9:58 
Arialdo Martini wrote:
>a fool proof way of detecting when code is being run from within the designer so certain method calls can be avoided if need be (though that detection code would be nice regardless).
 
It sounds interesting. I still have no idea about how to do it.

 
For that you need to check the Component.DesignMode property.
 

Generalkind of workmembervikas maan9 Jan '08 - 17:53 
Hi,
Hey what kind or freelancing work u r doing man.
 
Vikas Maan
Tektronix
India

GeneralCode is too wide [modified]mvpKarl Shifflett9 Jan '08 - 1:00 
You need to reedit the article and when you do, trim your code lines so that they not as wide.
 
When you reedit the article, if you find yourself scrolling right and left in the edit box, you need to find the offending line and reformated it.
 
I like the topic and use this method to overcode this limittino of Visual Studio.
 
The edit should take about 5 minutes and your article will be much more readable.
 
Cheers, Karl
 
My Blog | Mole's Home Page | Choosing WPF over ASP.NET
 

Just a grain of sand on the worlds beaches.

modified on Wednesday, January 09, 2008 7:06:06 AM

GeneralRe: Code is too widememberArialdo Martini9 Jan '08 - 1:21 
Thank you for the suggestion, Karl. I hope now it's ok.
 
Cheers!
 
Arialdo
 
--
Arialdo Martini

AnswerRe: Code is too wide - not anymore it's fixed!mvpKarl Shifflett9 Jan '08 - 1:23 
Arialdo,
 
Great job getting this sorted out. For you first article you did very well. All developers who dare to post here have a few glitches from time to time. It's all good.
 
Keep the articles coming and have fun too!
 
Cheers, Karl
 
My Blog | Mole's Home Page | Choosing WPF over ASP.NET
 

Just a grain of sand on the worlds beaches.


GeneralRe: LinksmvpKarl Shifflett9 Jan '08 - 1:31 
Arialdo,
 
You are reference several outside Internet resources, diaplying the URL's.
 
If you make this URL's link, readers can then simply click on them to view them.
 
You read up on the HTML ANCHOR TAG for property formatting. There is also a tool in the edit window to convert a url into a link.
 
If you reply to this meesge, look at the formatting options just aboe the line of smiley icons.
 
You'll see "link" and link[^|.
 
Just select your URL and press one of these tools and it will make your link for you. Most use the second option since it applies the TARGET property to ANCHOR tage. You can up on these in 5 minutes and you'll be an expert.
 
Cheers, Karl
 
My Blog | Mole's Home Page | Choosing WPF over ASP.NET
 

Just a grain of sand on the worlds beaches.


GeneralRe: LinksmemberArialdo Martini9 Jan '08 - 1:56 
Added tags. Thanks again! (In the meanwhile, I'm reading about Mole: thank you for the posts on your blog).
 
--
Arialdo Martini

GeneralRe: LinksmvpKarl Shifflett9 Jan '08 - 2:27 
Arialdo,
 
Thank you for the kind remards on Mole & blog. Smile | :)
 
I've been reading the referenced articles also. Very good information.
 
What is so interesting is that I'm currently going back and forth between using in Interface or a Abtstract class for an artilce I'm just about to post.
 
I have this base class for our usercontrols and WPF has the same issues as Winforms.
 
Your article and the releated article will help me in my decision.
 
Trying to balance correct OO, with daily simple use of the object, you know, that item, "Real world" programming.
 
Best to you,
 
Cheers, Karl
 
My Blog | Mole's Home Page | Choosing WPF over ASP.NET
 

Just a grain of sand on the worlds beaches.


General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 20 Jan 2008
Article Copyright 2008 by Arialdo Martini
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid