I've been trying for the past few hours to solve a problem that I don't understand really what is wrong. I'm building a GUI system for a project that needs multiple forms that are almost the same, but not quite. I figured I have one class (as show below) that inherits a standard, run-of-the-mill form.
class GUIWindow : Form, IGUIWindow
{
public GUIWindow() : base()
{
}
public GUIWindow(string name) : base()
{
this.Name = name;
}
}
and a second class (below) that inherits the first class, so that any change I make to the base class, it would affect all sub-classes, and there will be four or so sub-classes.
class GUIWindowText : GUIWindow, IGUIWindow
{
public GUIWindowText() : base() { }
public GUIWindowText(string name) : base()
{
this.Name = name;
}
}
But if I look at the designer in visual studio (2017), I get this error:
Constructor on type 'LCARS.GUIWindow' not found.
Even though I do provide an empty constructor. So I must be missing something obvious but I don't know what.
EDIT
It does compile fine. I just can't see the designer
Let Visual Studio create the inherited form: In your project right click in the Solution Explorer to open the context menu, Add > New Item > Inherited Form (in the Windows Forms section) and then click "Add". This opens a wizard that asks you for the base Form, choose GUIWindow and click OK. VS then sets up the derived form correctly.
Note: If you want to change controls existing the base form in the derived form (e.g. change their size or move them around), you must change their accessibility from private to protected.
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 KNEW there had to be a way to do this (and I found a way to do this cleanly). Sheng's solution is exactly what I came up with as a temporary workaround but after a friend pointed out that the Form class eventually inherited from an abstract class, we SHOULD be able to get this done. If they can do it, we can do it.
We went from this code to the problem
Form1 : Form
Problem
public class Form1 : BaseForm
...
public abstract class BaseForm : Form
This is where the initial question came into play. As said before, a friend pointed out that System.Windows.Forms.Form implements a base class that is abstract. We were able to find...
Proof of a better solution
Inheritance Hierarchy:
System.Object
System.MarshalByRefObject (public **abstract** class MarshalByRefObject)
System.ComponentModel.Component
System.Windows.Forms.Control
System.Windows.Forms.ScrollableControl
System.Windows.Forms.ContainerControl
System.Windows.Forms.Form
From this, we knew that it was possible for the designer to show a class that implemented a base abstract class, it just couldn't show a designer class that immediately implemented a base abstract class. There had to be at max 5 inbetween, but we tested 1 layer of abstraction and initially came up with this solution.
Initial Solution
public class Form1 : MiddleClass
...
public class MiddleClass : BaseForm
...
public abstract class BaseForm : Form
...
This actually works and the designer renders it fine, problem solved.... except you have an extra level of inheritance in your production application that was only necessary because of an inadequacy in the winforms designer!
This isn't a 100% surefire solution but its pretty good. Basically you use #if DEBUG to come up with the refined solution.
Refined Solution
Form1.cs
public class Form1
#if DEBUG
: MiddleClass
#else
: BaseForm
#endif
...
MiddleClass.cs
public class MiddleClass : BaseForm
...
BaseForm.cs
public abstract class BaseForm : Form
...
What this does is only use the solution outlined in "initial solution", if it is in debug mode. The idea is that you will never release production mode via a debug build and that you will always design in debug mode.
The designer will always run against the code built in the current mode, so you cannot use the designer in release mode. However, as long as you design in debug mode and release the code built in release mode, you are good to go.
The only surefire solution would be if you can test for design mode via a preprocessor 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.
public class MyControl : AbstractControl
...
public abstract class AbstractControl : UserControl // Also works for Form
...
Now all we need is a Description provider.
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType == typeof(TAbstract))
return typeof(TBase);
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
{
if (objectType == typeof(TAbstract))
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
Finally we just apply a TypeDescriptionProvider attribute to the Abastract control.
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]
public abstract class AbstractControl : UserControl
...
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
<appSettings>
<add key="EnableOptimizedDesignerReloading" value="false" />
</appSettings>
Thanks #user3057544 for the suggestion.
#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):
public class Form1
#if DEBUG
: MiddleClass
#else
: BaseForm
#endif
I had a similar problem but found a way to refactor things to use an interface in place of an abstract base class:
interface Base {....}
public class MyUserControl<T> : UserControl, Base
where T : /constraint/
{ ... }
This may not be applicable to every situation, but when possible it results in a cleaner solution than conditional compilation.
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 be Form1). The TypeDescriptionProvider 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:
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<BaseForm, BaseFormMiddle2>))] // BaseFormMiddle2 explained below
public abstract class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
}
public abstract void SomeAbstractMethod();
}
public class Form1 : BaseForm // Form1 is the form to be designed. As you see it's clean and you do NOTHING special here (only the the normal abstract method(s) implementation!). The developer of such form(s) doesn't have to know anything about the abstract base form problem. He just writes his form as usual.
{
public Form1()
{
InitializeComponent();
}
public override void SomeAbstractMethod()
{
// implementation of BaseForm's abstract method
}
}
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 the TypeDescriptionProvider of BaseForm. No conditional compilation.
I was having two more problems:
Problem 1: After changing Form1 in designer (or some code) it was giving the error again (when trying to open it in designer again).
Problem 2: BaseForm's controls was placed incorrectly when the Form1's size was changed in designer and the form was closed and reopened again in the form designer.
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.
class BaseFormMiddle1 : BaseForm
{
protected BaseFormMiddle1()
{
}
public override void SomeAbstractMethod()
{
throw new NotImplementedException(); // this method will never be called in design mode anyway
}
}
class BaseFormMiddle2 : BaseFormMiddle1 // empty class, just to make the VS designer working
{
}
And here you have the slightly modified version of TypeDescriptionProvider:
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
return typeof(TBase);
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
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 to public class Form1 : BaseFormMiddle1 (or BaseFormMiddle2) 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.
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 have a tip for Juan Carlos Diaz solution. It works great for me, but was some problem with it. When I start VS and enter designer everything works fine. But after run the solution, then stop and exit it and then try to enter designer the exception appears again and again until restarting VS.
But I found the solution for it - everything to do is add below to your app.config
<appSettings>
<add key="EnableOptimizedDesignerReloading" value="false" />
</appSettings>
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 :
public class DataForm : Form {
protected virtual void displayFields() {}
}
public partial class Form1 : DataForm {
protected override void displayFields() { /* Do the stuff needed for Form1. */ }
...
}
public partial class Form2 : DataForm {
protected override void displayFields() { /* Do the stuff needed for Form2. */ }
...
}
/* Do this for all classes that inherit from DataForm. */
Since DataForm was supposed to be an abstract class with the abstract member displayFields, 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 of displayFields. Thus, be careful when using this technique.
The Windows Forms Designer is creating an instance of the base class of your form/control and applies the parse result of InitializeComponent. That's why you can design the form created by the project wizard without even building the project. Because of this behavior you also can not design a control derived from an abstract class.
You can implement those abstract methods and throw an exception when it is not running in the designer. The programmer who derive from the control must provide an implementation that does not call your base class implementation. Otherwise the program would crash.
You could just conditionally compile in the abstract keyword without interposing a separate class:
#if DEBUG
// Visual Studio 2008 designer complains when a form inherits from an
// abstract base class
public class BaseForm: Form {
#else
// For production do it the *RIGHT* way.
public abstract class BaseForm: Form {
#endif
// Body of BaseForm goes here
}
This works provided that BaseForm doesn't have any abstract methods (the abstract keyword therefore only prevents the runtime instantiation of the class).
I have a base class, defined as below (I'm also using DevExpress components):
public abstract partial class BaseFormClass<R> : XtraForm where R : DataRow
{
...
}
Contrary to what I've read from elsewhere, I'm still able to design this class. I didn't have to create a concrete class from it to do so. But, when I create a concrete class descended from it (as below), that class won't work in the designer.
public partial class ConcreteFormClass : BaseFormClass<StronglyTypedRow>
{
...
}
I get this message:
The designer could not be shown for
this file because none of the classes
within it can be designed. The
designer inspected the following
classes in the file:
ConcreteFormClass --- The base
class
'BaseFormClass'
could not be loaded. Ensure the
assembly has been referenced and that
all projects have been built.
Has anyone seen this before? Any sort of known workaround?
Sorry, but this just isn't going to work (which is a shame -- I've wished in the past that you could do this, too.) The problem is the basic methodology of the designer.
To present you with a model of your form, it doesn't actually try to construct the form itself; if it did that, you'd run into other problems -- what if your form doesn't have a parameterless constructor? Instead, it actually instantiates an instance of the base class of your form. Then it sweeps through your InitializeComponents() method and "layers on" all the controls that you've defined there onto the base form.
So it's obvious why this won't work. You can design an instance of BaseFormClass, because to design that, it creates an instance of XtraForm, which is concrete. But you can't design an instance of ConcreteFormClass, because to do so, it would need to create an instance of BaseFormClass, which is abstract.
The easiest workaround for this is to just make BaseFormClass non-abstract. (If you want to make absolutely sure nobody can create one, perhaps you could make the default constructor private? I'm not sure if the designer can handle that, but I don't see why it couldn't.) Sucks, but such is life. Complain to Microsoft and maybe it'll be better in Visual Studio 2012.
This sounds like a really similar issue to getting the designer to render forms that have an abstract base class. I haven't done any generic multi inheritance but you could at least try my approach and see if it works.
Edit: Yep, ok, just tried it, my solution works for sure. You just have to modify the middle classes definition and the forms definition (wrapped in the #if DEBUG)
Let me know if you're able to try it!
I have a class declared as:
public class Foo<T> : Panel where T : Control, IFooNode , new()
{
...
}
I added it by hand to test it, but I will eventually need something that can be displayed in the Forms designer. The Forms designer doesn't like this, it says:
Could not find type 'FooTestNameSpace.Foo'.
Please make sure that the assembly that
contains this type is referenced.
If this type is a part of your development project, make
sure that the project has been successfully built.
Interestingly, I ALSO get a warning that is similar, but includes the generic type I used in my declaration of the variable.
Warning is:
Could not find type 'FooTestNameSpace.Foo<FooTestNameSpace.FooNodeType>'.
Please make sure that the assembly that contains this type is referenced.
If this type is a part of your development project,
make sure that the project has been successfully built.
The declaration in my simple Form1 class is:
private FooTestNameSpace.Foo myFoo;
(FooNodeType is really just a subclass of Label that has one auxiliary property not yet being used; it does implement IFooNode).
So my question... With this type of set up, how can I get Foo to be displayed on the Forms designer, or at least get it to acknowledge that Foo is legit? Is there any Designer attribute that can handle this? I don't really care if my Foo control appears as an empty box, as long as it appears at all!
Ok, I think I have a solution:
class ConcreteFooCtl : FooCtl<FooNodeType>
{
}
class FooCtl<T> : Panel where T : Control, IFooNode , new()
{
}
The problem seems to be that the Forms designer can't handle any generic control.
The forms designer can't handle FooCtl, but has no problems with ConcreteFooCtl.
Not an ideal solution, but I think it's at least workable. Maybe the next version of VS will prompt the user to specify a generic type when adding a generic control to a form... ;)
Instead of making Foo a generic, you might be better of making it non generic, and having it contain a generic class that does what you need. You can then delegate all your processing from the form to the class.
I've made a class which inherits from UserControl, but which I only want to use as a base for subclasses. Is there a way I can stop VS2008 from trying to edit it in the designer, that won't stop the subclasses from being edited in the designer?
There is. I believe if you have multiple classes in a file, VS looks at the first one only, but I may be mistaken. In any case, this should do the trick:
[System.ComponentModel.DesignerCategory("Code")]
public class SomeBaseClass : UserControl
{
//...
}
Note that in versions of Visual Studio prior to 2017, you must use the full name of the attribute as shown above. If you try putting a using statement above it and simply trying "DesignerCategory" visual studio may not honor it.