
Just recently having started my first real life .Net 2.0 project I wanted to utilize all the cool new features in my day-to-day job. One of those features is of course the support for Generics in C#. But, as with all cool stuff, getting your hopes up to high can get you disappointed.
In our Windows forms application we use a design pattern in which a UserControl defines a callback interface which is implemented by a mediator. This makes sure we have a loose coupling between the UserControl and the source of the rest of the application.
To avoid having a lot of duplicate code I decided to make a base class that does some of the stuff related to initializing the mediator. While every control will have a different mediator interface this called for generics (sample code is written by hand, so don't mind the compiler errors).
class ViewControlBase : UserControl <TMediator>
where TMediator : IMediator
{
TMediator _mediator;
protected TMediator Mediator
{
get { return _mediator; }
}
public InitMediator(IMediator mediator)
{
_mediator = (TMediator) mediator;
// some more generic stuff
}
}
Now the viewControls are derrived from ViewControlBase like this
class CustomerView : ViewControlBase<ICustomerMediator>
{
// … Use typed access to the Mediator property
}
By using a generic base I was able to have Typed access to the Mediator in all the derived classes, and I avoided writing the initialization code over and over again. Hitting F5 proved all worked just as expected.
But when I opened one of the ViewControls in the designer the designer was filled with an error saying something was wrong. Usually this means that you have to rebuild the solution, clean up, rebuild some more and eventually it works. Not is time ๐
The error stated that the class CustomerView was not 'Designable' because it was not able to create an instance of ViewControlBase. That is true, it is however possible to create an instance of ViewControllerBase<TMediator>
I found a work-around (or a dirty Hack) by creating an empty dummy class like this
class CustomerView : CustomerViewDummy
{
//…
}
class CustomerViewDummy: ViewControlBase<ICustomerMediator>
{}
Now CustomerView is no longer (directly) derived from a generic class the designer stops complaining and shows the control in design view as expected. Since I didn't really like this solution I went back to old-school and duplicated the same code in every control (Bummer!)
10 comments
Sometimes I feel that designers are evil. While I understand your choice, and perhaps would have done the same, you break the DRY principle just because the designer support is lacking.
I’m ready for assembly programming! Who’s with me?
wouterv
I've experienced just that while working on my project trying to get some cool stuff to work with a workflow that can be designed by the user. Instead of a generics problem, I had a designer that didn't support my custom setstate activity inside a windows workflow foundation statemachine.
Designers are evil, especially when you are doing some special stuff to the classes that the designer relies upon.
Lets ditch the designer and go for a rather explosive mix of LINQ, generics and anonymous types. Gotta love new features ๐
Willem
From what I have seen so far the designers of Workflow foundation add little but the illusion that things are simpler than they actually are, in my opinion this is a bad thing.
For a GUI however the grapical representation does has the added value in that it shows you how the actual User interface will look like at runtime (more or less), this is a good thing, as long as it works.
I concur with Willem that powerfull language features will allways beat designers!
frankb
The WF-designer is a perfect example of an unfinished product being released, probably because commercial management couldn’t wait any longer.
Same for the standard activities, which are all marked sealed: “though we would like make our product as extensible as possible, we had to prioritize where we’d focus this extensibility for this version and we decided to focus at the component model level, instead of on the out-of-box activities.”. ๐
robertka
Frank ,
Nice trick, I thought this “bug” had been fixed in vs.net 2008 but i’ve just hit the same limitation.Your trick works great.
Thanks.
Sebastian Talamoni
I created a generic combo box which greatly simplified my code. But I nearly put my fist through my monitor when the WF designer choked on it. Your trick saved the day. Thanks!
BEM
Nice workaround. I can live with it, but still there might be problem again. Localize your base control and you’ll get in your designer file line such:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager( typeof( UserControl1 ) );
Well, we don’t have UserControl1 class anymore, so we have to change it to UserControl1. Now we can compile and open designer for that View, but … try to open inherited hacked UserControl2. You’ll get something like:
Could not find any resources appropriate for the specified culture or the neutral culture. Make sure “WindowsFormsTesting.UserControl1`1.resources” was correctly embedded or linked into assembly “WindowsFormsTesting” at compile time, or that all the satellite assemblies required are loadable and fully signed.
So, any ideas how to solve this new problem?
Regards,
BurmajaM
I've just figured out one mistyping. This is correction with forgotten generics:
"Well, we don't have UserControl1 class anymore, so we have to change it to UserControl1<T>"
BurmajaM
I am afraid I can't give you a direct answer to that one. As discussed before designers are not allways my best friends ๐
frankb
This is annoying to deal with but I'm surprised that you opted to duplicate code instead of using the workaround you outlined above if it worked for your scenario – I'm guessing the amount of code was smaller than the signature of the dummy class.
Here is an article that articulates some of the options the VS.NET team considered before coming to the conclusion that it could not be supported.
http://www.urbanpotato.net/default.aspx/document/1772
Matthew Erwin