I engaged a problem with inherited Controls in Windows Forms and need some advice on it.
I do use a base class for items in a List (selfmade GUI list made of a panel) and some inherited controls that are for each type of data that could be added to the list.
There was no problem with it, but I now found out, that it would be right, to make the base-control an abstract class, since it has methods, that need to be implemented in all inherited controls, called from the code inside the base-control, but must not and can not be implemented in the base class.
When I mark the base-control as abstract, the Visual Studio 2008 Designer refuses to load the window.
Is there a way to get the Designer work with the base-control made abstract?
I had a similar problem but found a way to refactor things to use an interface in place of an abstract base class:
This may not be applicable to every situation, but when possible it results in a cleaner solution than conditional compilation.
@Smelch, thanks for the helpful answer, as I was running into the same issue recently.
Following is a minor change to your post to prevent compilation warnings (by putting the base class within the
#if DEBUG
pre-processor directive):@smelch, There is a better solution, without having to create a middle control, even for debug.
What we want
First, let's define the final class and the base abstract class.
Now all we need is a Description provider.
Finally we just apply a TypeDescriptionProvider attribute to the Abastract control.
And that's it. No middle control required.
And the provider class can be applied to as many Abstract bases as we want in the same solution.
* EDIT * Also the following is needed in the app.config
Thanks @user3057544 for the suggestion.
Since the abstract class
public abstract class BaseForm: Form
gives an error and avoid the use of the designer, I came with the use of virtual members. Basically, instead of declaring abstract methods, I declared virtual methods with the minimum body as possible. Here's what I've done :Since
DataForm
was supposed to be an abstract class with the abstract memberdisplayFields
, I "fake" this behavior with virtual members to avoid the abstraction. The designer doesn't complain anymore and everything works fine for me.It's a workaround more readable, but since it's not abstract, I have to make sure that all child classes of
DataForm
have their implementation ofdisplayFields
. Thus, be careful when using this technique.I'm using the solution in this answer to another question, which links this article. The article recommends using a custom
TypeDescriptionProvider
and concrete implementation of the abstract class. The designer will ask the custom provider which types to use, and your code can return the concrete class so that the designer is happy while you have complete control over how the abstract class appears as a concrete class.Update: I included a documented code sample in my answer to that other question. The code there does work, but sometimes I have to go through a clean/build cycle as noted in my answer to get it to work.
I've got some tips for people who say the
TypeDescriptionProvider
by Juan Carlos Diaz is not working and don't like the conditional compilation neither:First of all, you may have to restart Visual Studio for the changes in your code to work in the form designer (I had to, simple rebuild didn't work - or not every time).
I will present my solution of this problem for the case of abstract base Form. Let's say you have a
BaseForm
class and you want any forms based on it to be designable (this will beForm1
). TheTypeDescriptionProvider
as presented by Juan Carlos Diaz didn't work for me also. Here is how I made it work, by joining it with the MiddleClass solution (by smelch), but without the#if DEBUG
conditional compiling and with some corrections:Notice the attribute on the BaseForm class. Then you just have to declare the
TypeDescriptionProvider
and two middle classes, but don't worry, they are invisible and irrelevant for the developer of Form1. The first one implements the abstract members (and makes the base class non abstract). The second one is empty - it's just required for the VS form designer to work. Then you assign the second middle class to theTypeDescriptionProvider
ofBaseForm
. No conditional compilation.I was having two more problems:
The first problem (you may not have it because it's something that haunts me in my project in few another places and usually produces a "Can't convert type X to type X" exception). I solved it in the
TypeDescriptionProvider
by comparing the type names (FullName) instead of comparing the types (see below).The second problem. I don't really know why the base form's controls are not designable in Form1 class and their positions are lost after resize, but I've worked it around (not a nice solution - if you know any better, please write). I just manually move the BaseForm's buttons (which should be in bottom-right corner) to their correct positions in a method invoked asynchronously from Load event of the BaseForm:
BeginInvoke(new Action(CorrectLayout));
My base class has only the "OK" and "Cancel" buttons, so the case is simple.And here you have the slightly modified version of
TypeDescriptionProvider
:And that's it!
You don't have to explain anything to the future developers of forms based on your BaseForm and they don't have to do any tricks to design their forms! I think it's the most clean solution it can be (except for the controls repositioning).
One more tip:
If for some reason the designer still refuses to work for you, you can always do the simple trick of changing the
public class Form1 : BaseForm
topublic class Form1 : BaseFormMiddle1
(orBaseFormMiddle2
) in the code file, editing it in the VS form designer and then changing it back again. I prefer this trick over the conditional compilation because it's less likely to forget and release the wrong version.