(Print this page)

Issues with Generic Windows Forms inheritance (and the solution)
Published date: Sunday, February 28, 2016
On: Moer and Éric Moreau's web site

I had this issue last week. I wanted to use a generic form but the designer (very useful in Windows Forms) was always reporting a strange error even if the compilation was not reporting any issues.

If you ever tried, you surely had the issue.

If you never tried, read on, this is a great feature and the workaround to make the designer is really easy!

Downloadable demo code

The downloadable demo code this month is provided once again in both VB and C#. The solution was created using Visual Studio 2015 and the .Net Framework 4.6.1 but Generics are around since the .Net Framework 2.0.

Why generics?

As you already know, generics let you use typed members without really knowing the exact type until runtime. It lets you save a lot of code. And it prevents you from using other mechanism such as Reflection.

If you never looked at generics, better start by looking at https://msdn.microsoft.com/en-us/library/ms172192(v=vs.110).aspx and https://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx.

I have been using generic classes for a long time but never had to deal with generic forms which in my mind would have been just the same but the designer decided otherwise.

Issue with generic forms inheritance

The .Net Framework itself has no issues at all with you inheriting a generic form because for the framework, a form is just another class.

The problem lies in Visual Studio and more specifically, the forms’ designer.

If you try to open the designer of a form inheriting a generic form, chances are that you will see either the error shown in figure 1 or 2 (depending on the language you are using). The error is reported differently depending on the language but the result is the same, you won’t see your form!

Figure 1: The Windows Forms designer hardly refusing to show a form inheriting a generic one



Figure 2: Same issue but from a C# project



Chances are that if you test it by yourself, you will first create a generic form and then create a second form to inherit from the base one. You will probably double-click to open the child form from the designer and you might see it.

At this point you will surely think that I am a liar. But play a bit with your forms, the base one and the child one. Sooner or later, you will eventually get the error.

Let’s recreate a project and show how to fix it.

Creating the demo project

I don’t want something too complex. Just enough to be able to demonstrate the error and how to fix it.

I will create a project with a menu form (2 buttons to show a Client or an Employee form), a base generic form with a button using the Generic type to show the type and a value from the generic class (MyGenericForm) and 2 children inheriting from it creating the specific instance of a class and passing if to the form (fClient and fEmployee). And might seems complex but it is not.

Figure 3: The demo application in action



Creating the classes

We will just create 3 little classes. The first one is an abstract one, exposing a single read-only property and is named cBaseEntity and looks like this:

Namespace Classes
 
    Public MustInherit  Class cBaseEntity
         
        public MustOverride ReadOnly Property Name() As String
 
    End Class
 
End NameSpace

The next 2 classes are inheriting from cBaseEntity and are specialized for a client and for an employee. For my demo, I simply want to return a value to prove the class we are using.

This is the code for the cClient class:

Namespace Classes
 
    Public Class cClient
        Inherits cBaseEntity
 
        Public Overrides ReadOnly Property Name As String
            Get
                Return "Client"
            End Get
        End Property
 
    End Class
 
End Namespace

This is the code for the cEmployee class:

Namespace Classes
 
    Public Class cEmployee
        Inherits cBaseEntity
 
        Public Overrides ReadOnly Property Name As String
            Get
                Return "Employee"
            End Get
        End Property
 
    End Class
 
End Namespace

Nothing is related to generics so far. Let’s move on.

Creating the base generic form

The first form to be created will be the one becoming the generic one. Create a regular form (I named mine MyGenericForm). Add a button to the form and handle the Click event of the button (check the code below to see what we want to show). Finally, add the generic clause to your form definition (the part after the form name showing “(of T As {cBaseEntity} )”). Your code should look like this:

Imports DemoGenericWinFormsVB.Classes
 
Public partial Class MyGenericForm(of T As {cBaseEntity} )
 
    Protected ClassInstance as T
 
    Private Sub btnGenericForm_Click(sender As Object, e As EventArgs) Handles btnGenericForm.Click
        MessageBox.Show("Generic form showing type " + GetType(T).ToString() + " - Name=" + ClassInstance.Name)
    End Sub
 
End Class

For C# developers, the class declaration should read like this:

public partial class MyGenericForm<T> : Form where T : cBaseEntity

If you stop there and you check your error list window, you will find errors. This is because you have changed the signature of the class and you need to keep the other signature found in the .designer file on par. Click on your form name in the code editor and hit F12. That should open the code editor for the .designer file. Add the same generic clause after the form name. It should look like this:

Partial Class MyGenericForm(of T As {cBaseEntity} )
    inherits System.Windows.Forms.Form

C# developers also need to add the generic part to the .designer file but don’t have to repeat the restriction to the cBaseEntity type.

partial class MyGenericForm  <T>

Now, all the errors should be removed.

Creating children forms

You probably already have worked with inherited forms. The first part is exactly the same. First we need to add a new form to our project (fClient for example). Once added, we need to add the Inherits clause. So I always add it to the form’s code so it looks like this:

Imports DemoGenericWinFormsVB.Classes
 
Public Class fClient
    Inherits MyGenericForm(Of cClient)
End Class

Because the .designer.vb file instructs the class to inherits from System.Windows.Forms.Form and .Net can only inherits from a single class, the error list will complain that base classes are different. Click on your class name in the code editor and hit F12 to open the .designer.vb file. Delete the Inherits statement from there and all compilation errors should go away.

For this part, C# developers don’t have anything special to do. The first code Window contains the inheritance and no modification to the .designer.cs file is required. That means the form’s code needs to be changed from:

public partial class fClient : Form

To:

public partial class fClient : MyGenericForm<cClient>

At this point, you might to open the child form designer and chances are good that you will see it correctly as explained before. But it won’t take long before the error shows up!

Fixing the issue

There is a solution to the designer issue. The solution is somewhat easy. All you need is a little class definition (that you will surely keep empty) just to fool the Visual Studio designer.

The only thing that this little class is doing is to sit between the generic base class and the child instance. You define one like this in VB:

Public class MyGenericFormOfClientBase
    Inherits MyGenericForm(of cClient)
End Class

Or like this in C#:

public class MyGenericFormOfClientBase : MyGenericForm<cClient> { }

I always add these little class at the bottom of my generic class (but you can add them almost anywhere in your project (except at the top of a form that requires a designer).

You now have to change the fClient to inherits from this little class:

Public Class fClient
    Inherits MyGenericForm(Of cClient)
End Class

Starting from now, the designer shouldn’t cause you problems.

Conclusion

This is an example of what can happens when the tooling improves over the time. Some part of it might have issues trying to follow. The designer exists since day one and the generic feature only appeared at v 2.0. at least, we have a workaround that is not too demanding!


(Print this page)