4

I have a base class

internal partial class View<T> : UserControl
  where T : class
{
    protected T t;
}

and I want to derive a child from View

internal partial class ViewChild<T> : View<T>
  where T : class
{
}

it works OK, but I cannot edit ViewChild in the VS designer. I know the problem is generic base class. However I do not understand how I can avoid that in this case. Is there any way to fix that?

wince
  • 511
  • 1
  • 5
  • 13

2 Answers2

3

There is another way, and it doesn't rely on compiler flags:

http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/

I really wouldn't advise the use of conditional compilation. Much better to work with the framework, and not against it.

Basically, you can give VS a different class through the existing framework. You decorate your base class with a TypeDescriptionProvider attribute which tells VS to use a different class as a designer.

As mentioned in the original blog post, there may be caveats associated with this workaround, but I got it working neatly on a project with > 25 UserControls inheriting from a common base class.

Mel Padden
  • 983
  • 1
  • 9
  • 21
  • 1
    That's a nice solution, but it doesn't mention generics. Did your use of it cover generics? – Adam Houldsworth Apr 18 '12 at 08:21
  • The problem domain is actually the same, i.e. VS hosting instances of the designed type. The reason VS moans about generics is that it can't instantiate them, like abstract classes. In this respect, all generics are pseudo-abstract; they make no sense to the runtime without their type parameters. To answer your question, I did briefly use generic base classes for an unfinished implementation of strongly-typed views (in Winforms), but as of right now I've retired that particular architectural diversion in favour of implementing real functionality. I'll return to it probably end of the month. – Mel Padden Apr 18 '12 at 08:25
  • +1 If it get time this weekend I'll try updating my old code with it, see if it lets me design the base class - good find. – Adam Houldsworth Apr 18 '12 at 08:29
  • I can't get that to work for two abstract classes :-( do you have a link to any code with a demonstration of the solution? – Adam Houldsworth Apr 18 '12 at 08:51
  • Thanks ;-p I will say, I find VS2010 a bit skittish when it's asked to do these things, and its behaviour is not predictable. I had to restart it a couple of times when implementing this. it helped when I installed some extensions. In general, actually, I find extensions in VS a pain, and unneccessary, so it was no biggie. Another thing to watch for is that if you inherit from a third party user-control (I'm looking at you, ComponentOne for WinForms) things get sticky performance-wise. Still, got it to work in the end, and it meant I didn't need #if blocks everywhere. – Mel Padden Apr 18 '12 at 08:53
  • I'll try to get back to it this weekend and post some working code... Right now I'm away from the machine I used. – Mel Padden Apr 18 '12 at 09:41
2

Generics break the designer because it cannot instantiate the class without a type T. I explain a workaround in my blog post:

http://adamhouldsworth.blogspot.co.uk/2010/02/winforms-visual-inheritance-limitations.html

In short, you need to "resolve" the type with an intermediary class:

  • BaseControl<T> : UserControl
  • CustomerControl_Design : BaseControl<Customer>
  • CustomerControl : CustomerControl_Design

You can then conditionally switch this class out of the code based on the DEBUG or RELEASE compiler switches:

#if DEBUG

namespace MyNamespace
{
    using System;


    public partial class CustomerEditorControl_Design : BaseEditorControl<Customer>
    {
        public CustomerEditorControl_Design()
            : base()
        {
            InitializeComponent();
        }
    }
}

#endif

    public partial class CustomerEditorControl
#if DEBUG
        : CustomerEditorControl_Design
#else
        : BaseEditorControl<Customer>
#endif
    {
    }

This will let you open the derived class of CustomerControl, unfortunately you will never be able to design a UI control with generics in the signature. My solution is only enabling the design of derived items.

I have no idea why CustomerControl : BaseControl<Customer> won't work as in this case the type T is defined, but it simply doesn't - I'm guessing because of the rules of generic usage.

To their defense, Microsoft do say that this isn't supported.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
  • As I understand this will work only for concrete Customer type and if I want to use ViewChild with different types it will not work. I have some base code into View, when I extend ViewChild with some extra code and I use View, ViewChild, ViewChild etc – wince Apr 18 '12 at 08:18
  • @wince You are correct. As I state in the answer it only solves the problem for derived forms. If you have a generic base form that actually has content I'd suggest binning that idea and doing something like composition in a shell form. In my usage, the base controls only provided logic, not visuals. – Adam Houldsworth Apr 18 '12 at 08:21
  • @wince Does your `View` and `ViewChild` also have any visual elements or is it just code? – Adam Houldsworth Apr 18 '12 at 08:24
  • View has only code, ViewChild is a Wizard form and has some buttons (back, next, cancel) and I need many objects of ViewChild with different T (presenters) – wince Apr 18 '12 at 08:36
  • I have found solution. Not good but it works. for design time i just use internal class ViewChild : UserControl – wince Apr 18 '12 at 08:57