VS2008 Form designer does not open form with generic types

A

Andrus

I created WinForms form containing generic DataGridView on and some other
controls.
Forms designer will not open this form anymore.
Error text is shown below. Application runs this form OK, rebuild all does
not fix.
I tried to change form base class to non-generic class but this does not
help.

I need to add new button to this form.
Manually changing designer generated code is difficult.
Any idea ?

Andrus.

Error which I got:

Warning 1 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:

BrowseForm --- The base class 'MyApp.ARBusinessBaseForm<T>' could not be
loaded. Ensure the assembly has been referenced and that all projects have
been built. 0 0


at
System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.EnsureDocument(IDesignerSerializationManager
manager)
at
System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager
manager)
at
Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager
serializationManager)
at
Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.DeferredLoadHandler.Microsoft.VisualStudio.TextManager.Interop.IVsTextBufferDataEvents.OnLoadCompleted(Int32
fReload)
 
N

Nicholas Paldino [.NET/C# MVP]

Andrus,

You can't use generic types with the designer. The designer doesn't
know how to instantiate the instance because it doesn't know what to use for
the type parameters.

You simply have to change the code by hand, or you have to make it
non-generic.
 
A

Andrus

How to solve ?

Is it possible and reasonable to use #ifdef to create non-generic version
for designer ?

Or must I remove all generic types manually from code to invoke designer and
put them back after using designer ?

Andrus.

Nicholas Paldino said:
Andrus,

You can't use generic types with the designer. The designer doesn't
know how to instantiate the instance because it doesn't know what to use
for the type parameters.

You simply have to change the code by hand, or you have to make it
non-generic.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Andrus said:
I created WinForms form containing generic DataGridView on and some other
controls.
Forms designer will not open this form anymore.
Error text is shown below. Application runs this form OK, rebuild all
does
not fix.
I tried to change form base class to non-generic class but this does not
help.

I need to add new button to this form.
Manually changing designer generated code is difficult.
Any idea ?

Andrus.

Error which I got:

Warning 1 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:

BrowseForm --- The base class 'MyApp.ARBusinessBaseForm<T>' could not be
loaded. Ensure the assembly has been referenced and that all projects
have
been built. 0 0


at
System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.EnsureDocument(IDesignerSerializationManager
manager)
at
System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager
manager)
at
Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager
serializationManager)
at
Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.DeferredLoadHandler.Microsoft.VisualStudio.TextManager.Interop.IVsTextBufferDataEvents.OnLoadCompleted(Int32
fReload)
 
M

Marc Gravell

Well, how much of the form uses generics? One approach could be to
have a standard form/control for the designer, i.e.

public class Foo : Form {...}

And then put all the generic stuff into a subclass:

public class Bar<T> : Foo {... stuff involving T; just a flat code
file...}

For designer stuff you use Foo; for code (and instantiation) you look
mainly at Bar<T> as code.

Since they are *essentially* the same form (so no encapsulation fuss),
I suppose you could make the controls available (protected) either at
the field or property level.

Marc
 
M

Marc Gravell

(minor; since Foo and Foo<T> resolve separately, you could name them
such without issue)
 
A

Andrus

Since they are *essentially* the same form (so no encapsulation fuss),
I suppose you could make the controls available (protected) either at
the field or property level.

I have deep form hiearchy:

Form
BaseForm<T>
BusinessForm<T>
BrowseForm<T>

etc.
and every level need to be designed.
So your solution requires:

1. Add wrapper classes:

Form
BaseForm<T>
BaseFromWrapper
BusinessForm<T>
BusinessFromWrapper
BrowseForm<T>
BrowseFormWrapper

2. Change visibility of members from private to protected only to satisfy
designer.

The other solution wound to remove generic types, pass Type t as parameter
and use reflection inctead of generic. As far as I now passing type
parameter + using reflection can
implement every generic types feature, is it so ?

Which is best way ?

Andrus.
 
M

Marc Gravell

I have deep form hiearchy:

Fair enough; then don't use the approach I mentioned; 'twas only an
idea (and you didn't mention the hierarchy... I tend to avoid overuse
of inheritance in this scenario, as I rarely find it helps - but if it
helps you, great).
2. Change visibility of members  from private to protected only to satisfy
designer.

Actually it was only to make it possible for the generic part of the
class to access the controls; the designer wouldn't be used with the
generic form, so it is nothing to do with the designer.
As far as I now passing type
parameter + using reflection can
implement every generic types feature, is it so ?

Pretty much - just not nearly as convenient nor elegant, and without
compile-time constraint checking.

Marc
 
A

Andrus

Well, how much of the form uses generics?

Form type parameter parameter is DLinq entity type T
Form contains generic grid with same type parameter T:

partial class BrowseForm<T> {
....
private MyApp.Grid<T> grid;
}

One approach could be to
have a standard form/control for the designer, i.e.

public class Foo : Form {...}

And then put all the generic stuff into a subclass:

public class Bar<T> : Foo {... stuff involving T; just a flat code
file...}

This allows to wrap BrowseForm<T> only.
How to create non-generic version of Grid<T> member ?

Andrus.
 
N

Nicholas Paldino [.NET/C# MVP]

You might want to consider making all of your types non-Generic, and
adding a method that is called which you can set the type that the form uses
for browsing and whatnot, something like:

SetBrowseType(Type type)

And have a generic overload on the method as well:

SetBrowseType<T>()

It seems you are using the type parameter to convey type information,
which is poor design if you don't have a non-generic alternative.

I would have recommended passing the type in the constructor, but that
would have broken designer support as well =)

Depending on how much actual functionality you expose through generics,
this might be a simpler solution for you.
 
A

Andrus

Fair enough; then don't use the approach I mentioned; 'twas only an
idea (and you didn't mention the hierarchy... I tend to avoid overuse
of inheritance in this scenario, as I rarely find it helps - but if it
helps you, great).

I can flatten hieararchy if you think this is better.
Framework which I studied uses 5 level form hiearchy. So I thought that
this is good design and tried to implement my forms in similar way.

Using your hint I re-looked into my form hiearchy. Some classes contain few
methods so they can be eliminated.
I'm thinking about the following hiearchy:

Form - net base class
Baseform - contains base routines for every form used in application:
form position save-restore etc.
BusinessForm<T> ( contains Grid<T> )
- contains common logic and controls for single linear
entity type
(Customer, Product etc). editing.
BrowseForm<T> - contains few additional buttons specific for
some entities.

Is this best design or should I simplify it more ?

Andrus.
 
A

Andrus

Nicholas,
You might want to consider making all of your types non-Generic, and
adding a method that is called which you can set the type that the form
uses for browsing and whatnot, something like:

SetBrowseType(Type type)

My planned hiearchy looks like:

Form - net base class
Baseform - contains base routines for every form used in application:
form position save-restore etc.
BusinessForm<T> ( contains Grid<T> )
- contains common logic and controls for single linear
entity type
(Customer, Product etc). editing.
BrowseForm<T> - contains few additional buttons specific for
some entities.

So if I understand your recommendation properly, I must change type T to
Form and Grid constructor parameter. I see no reason to use SetBrowseType()
since type can be passed in constructor. Why SetBrowseType() is better than
using BrowseForm(Type t) constuctor ?
And have a generic overload on the method as well:

SetBrowseType<T>()

Why this is better than using Marc hint to create generic wrapper for
designer class:

class BrowseForm<TEntity>: BrowseForm {
public BrowseForm<TEntity>( .... ): base(typeof(TEntity), ..... ) { ...}
}

BrowseForm<TEntity> makes few usagw of type T, most usage is in grid.
So I think I must use only non-generic BrowseForm and drop generic classes
at all?
It seems you are using the type parameter to convey type information,
which is poor design if you don't have a non-generic alternative.

T is linear entity type (Customer, Produce, Emplyee etc) which is edited in
form in Grid.
Is it poor design only since it is not supported by Designer ?
Will creating non-generic intermediate class only to satisfy designer make
it good design ?
I would have recommended passing the type in the constructor, but that
would have broken designer support as well =)

Is it reasonable to create parameterless constructor only for designer
support :

class BrowseForm {
// Real constructor
public BrowseForm(Type entityToEdit, ..... ) { ...}

// Empty constructor for designer support, prevent its use in code.
public BrowseForm() { Debug.Assert(false); }
}

In this case SetBrowseType() method is not required and designer can open
form.

Unresolved issue is using Grid<T> in designer.

As I understand only way is to change this grid to non generic Grid( Type
t ) and add parameterless constructor to Grid to satisfy designer.
In this case I lose compile time checking of type parameter constraints and
must use heavy
reflection in Grid class.
Is this best solution to create generic entity editing grid whose containg
form can be designed in designer?

Or should I stop using designer and design forms manually to keep code clean
?

Andrus.
 
M

Marc Gravell

Unresolved issue is using Grid said:
...
Or should I stop using designer and design forms manually to keep code clean ?

Maybe use the designer for general layout, but leave a Panel as a
placeholder for you grid; have an Initialise<T>() method with whatever
arguments and constraints you need, and just setup the grid in this
generic method - i.e.

Initialise<T>() {
Grid<T> grid = new Grid<T>();
grid.Dock = {blah}.Fill;
gridPanel.Controls.Add(grid);
// etc
}


Marc
 
N

Nicholas Paldino [.NET/C# MVP]

Andrus,

See inline:
My planned hiearchy looks like:

Form - net base class
Baseform - contains base routines for every form used in application:
form position save-restore etc.
BusinessForm<T> ( contains Grid<T> )
- contains common logic and controls for single linear
entity type
(Customer, Product etc). editing.
BrowseForm<T> - contains few additional buttons specific for
some entities.

So if I understand your recommendation properly, I must change type T to
Form and Grid constructor parameter. I see no reason to use
SetBrowseType() since type can be passed in constructor. Why
SetBrowseType() is better than using BrowseForm(Type t) constuctor ?

As I mentioned, you will not have designer support if you don't have a
default constructor. Since that is the aim here, having a constructor which
takes the Type is a little bit of a waste, since you will have to add a
method which will take the Type anyways (having the default constructor
^just^ for designer support is a bad idea).
Why this is better than using Marc hint to create generic wrapper for
designer class:

class BrowseForm<TEntity>: BrowseForm {
public BrowseForm<TEntity>( .... ): base(typeof(TEntity), ..... ) { ...}
}

BrowseForm<TEntity> makes few usagw of type T, most usage is in grid.
So I think I must use only non-generic BrowseForm and drop generic classes
at all?

That could work, but it depends on how much the Grid<T> and the
BrowseForm<T> are related. If the Grid expects to access elements on the
parent for some reason, then you are adding this extra constraint that there
be this wrapper class associated with your form which could cause some more
headaches.
T is linear entity type (Customer, Produce, Emplyee etc) which is edited
in form in Grid.
Is it poor design only since it is not supported by Designer ?
Will creating non-generic intermediate class only to satisfy designer make
it good design ?

I say it is poor design because it appears that the only reason you are
passing T is to get type information from T. I don't know if the type T has
any constraints on it, so I don't know if you are doing anything with T
other than reflecting on it to get specific type information (if you have
constraints, you aren't reflecting most likely, but you aren't indicating
that you do have constraints).

Having ^just^ a generic method where the type parameter is used ^solely^
for the purpose of conveying type information is bad design. You would want
an additional method which would pass an instance of Type to convey the type
information, because the code that calls your code might not always have
type information at compile time.
Is it reasonable to create parameterless constructor only for designer
support :

class BrowseForm {
// Real constructor
public BrowseForm(Type entityToEdit, ..... ) { ...}

// Empty constructor for designer support, prevent its use in code.
public BrowseForm() { Debug.Assert(false); }
}

In this case SetBrowseType() method is not required and designer can open
form.

I would say no, because then you are polluting your design and with the
possibility that someone could call the parameterless constructor, you put
them in a situation where the form is unusable because they can't set the
type information that they would call in the constructor.
Unresolved issue is using Grid<T> in designer.

As I understand only way is to change this grid to non generic Grid( Type
t ) and add parameterless constructor to Grid to satisfy designer.
In this case I lose compile time checking of type parameter constraints
and must use heavy
reflection in Grid class.
Is this best solution to create generic entity editing grid whose containg
form can be designed in designer?

The same issues with the Grid<T> are the ones you are facing here,
really. You need a way to convey the type information on the parent to the
Grid and make it non generic (if you want designer support).
Or should I stop using designer and design forms manually to keep code
clean ?

Well, that depends on what your pain threshold is. If it's a simple
form, then maybe it will work, but if it is something more complex (which I
imagine it is), then you probably are going to hurt yourself a great deal by
eliminating designer support.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top