Using Visual Studio Whidbey to Design Abstract Forms

Brian in Coding | 38 Comments February 20, 2005

A long time ago I posted an article explaining why Visual Studio can’t design abstract forms. I also promised that I’d show you a way you could make it work in Whidbey using Whidbey’s type description provider mechanism. Well, a long time has passed and I never wrote the follow-up. It’s time I fulfill my promise.


Note: While this is a cool and funky example of the power you wield with custom type description providers, Microsoft doesn't support abstract base classes in the designer, so if you use this technique in your own code, you are on your own.

Update: There is a bug in Visual Studio Beta 2 that may prevent this code from working. I fixed the bug in post Beta 2 builds of VS, but unfortunately the fix did not make it into Beta 2. You may want to keep this code in your back pocket until VS is finally out the door.


Type Description Providers

The heart of my mechanism is a custom TypeDescriptionProvider. I need to give you a little background on this new and powerful way of providing metadata to designers. First, while Visual Studio does use reflection to get to the properties and events on objects, it never uses System.Reflection directly. Instead, it uses a class called TypeDescriptor which is found in the System.ComponentModel namespace. In Visual Studio 7.0 and 7.1, TypeDescriptor was built on top of System.Reflection. It provided a simpler API for designers to use, and it also provided a simple extensibility mechanism through the ICustomTypeDescriptor interface.

We have completely redesigned TypeDescriptor’s internals for Visual Studio Whidbey. Instead of being built on reflection, TypeDescriptor is now built on TypeDescriptionProviders. There is a default TypeDescriptionProvider that uses reflection, but anyone can attach a provider to any class or instance they choose. This provides you with a huge increase in flexibility, as you now have control over the implementation of the entire TypeDescriptor API.

I’m going to write a custom TypeDescriptionProvider for my abstract form that takes some “creative liberties” with type metadata.

The Abstract Form

In my example, I have an abstract form called, not surprisingly, “AbstractForm”. It also defines a single abstract property called AbstractProperty:

public abstract class AbstractForm : Form {
    public abstract string AbstractProperty { get; set; }
}

It is impossible to create an instance of this class unless I derive from it and implement the abstract property. So, I have a class that does that too:

internal class ConcreteForm : AbstractForm {
    string _abstractProperty;

    public override string AbstractProperty {
        get { return _abstractProperty; }
        set { _abstractProperty = value; }
    }
}

My goal is to make things that derive from AbstractForm really look like they’re deriving from ConcreteForm. My first step in doing this is to put an attribute on AbstractForm that declares that it, and all classes that derive from it, should receive a custom type description provider:

[TypeDescriptionProvider(typeof(ConcreteClassProvider))]
public abstract class AbstractForm : Form {…}

Now I need to write a ConcreteClassProvider that does all of the dirty work. I’ll cover that in the next section.

The ConcreteClassProvider Class

When I associated my type description provider to AbstractForm above, what I’m telling TypeDescriptor is that I know how to supply all metadata for AbstractForm and all of its derived types. If I had to write all of that logic myself it would be very complex and I probably wouldn’t get it right. Luckily, TypeDescriptionProvider has a constructor that takes an existing provider and delegates to it. By using this constructor all I have to do is override the areas I’m interested in and the rest will be taken care of for me. So, let’s dig in and write the skeleton for ConcreteClassProvider:

internal class ConcreteClassProvider : TypeDescriptionProvider {

    public ConcreteClassProvider() : 
        base(TypeDescriptor.GetProvider(typeof(AbstractForm))) {
    }
}

There are a couple of noteworthy things about this code:

  1. It is internal, but its constructor is public. TypeDescriptor does not require your class to be public, but it does require the constructor to be public. I’ve made the class internal because “there are no user-serviceable parts inside”. It just isn’t interesting for anyone other than TypeDescriptor to get at this class, and TypeDescriptor always gets at it through its public base class.
  2. In my constructor, I invoked the base class’s constructor and I passed in the current type description provider for AbstractForm. In this way my custom provider behaves no differently than the default provider except for the places where I override methods.

I’ve now setup a way that I can hook into the metadata that is offered by AbstractForm. One thing to keep in mind is that I am actually hooked into providing metadata for AbstractForm as well as all classes that derive from it. I have to be careful here. A simple mistake could make all derived classes look just like AbstractForm, which isn’t what I want!

There are several virtual methods on TypeDescriptionProvider, but I’m only concerned with one thing: I want ConcreteForm to replace AbstractForm. To do this there are two methods I need to override: GetReflectionType and CreateInstance. Let’s cover each of these in turn.

When the form designer needs to query type information, such as type.IsAbstract, for example, it always uses TypeDescriptor.GetReflectionType to obtain the type it should be reflecting against. This method finds the TypeDescriptionProvider for the type and calls, not surprisingly, GetReflectionType on the provider. The default provider simply returns the type passed into it. I want my provider to return ConcreteForm so that the designer thinks it is designing a concrete class:

public override Type GetReflectionType(Type objectType, object instance) {

    if (objectType == typeof(AbstractForm)) {
        return typeof(ConcreteForm);
    }

    return base.GetReflectionType(objectType, instance);
}

With this code the designer no longer thinks forms derived from AbstractForm are abstract classes. Instead, it sees ConcreteForm, which is not abstract.

That’s half the story. As soon as the designer determines that the base class isn’t abstract it will try to create an instance of it. That will lead to problems – it will try to create an instance of AbstractForm, fail, and put me right back where I started. That’s where the second override comes in. I need to override CreateInstance so I create an instance of ConcreteForm, not AbstractForm:

public override object CreateInstance(IServiceProvider provider, 
                                      Type objectType, 
                                      Type[] argTypes, 
                                      object[] args) {

    if (objectType == typeof(AbstractForm)) {
        objectType = typeof(ConcreteForm);
    }

    return base.CreateInstance(provider, objectType, argTypes, args);
}

Now I’m ready to try it out. After building the project I invoked the New Inherited Form dialog. What’s this? I don’t see AbstractForm as an option in the dialog. It turns out that the inheritance dialog doesn’t use GetReflectionType – it should, and I’ll follow up on that. Don’t despair, though, we just need to fiddle with the code a little bit. Instead, I created a standard Form, opened the code, and changed the form to derive from AbstractForm. When I opened the designer…it actually opened. What’s more, I can even change the value of AbstractProperty in the property window. Of course, as my new form is actually deriving from AbstractForm I do get compile errors unless I implement AbstractProperty on my new form class. That’s actually great news – it is why I wanted an abstract class in the first place.

Generalizing The ConcreteClassProvider Class

This mechanism works, but it only works for a single class. What happens if I want to support lots of abstract classes? I wouldn’t want to write a ConcreteClassProvider for each and every class, would I? Instead, I want to generalize ConcreteClassProvider so it can be used to provide a concrete class for any abstract class.

The challenge lies in getting our general-purpose ConcreteClassProvider to know what concrete class should be used for a given abstract class. To do this, I’ve defined a new attribute called ConcreteClassAttribute:

[AttributeUsage(AttributeTargets.Class)]
internal class ConcreteClassAttribute : Attribute {
    Type _concreteType;

    public ConcreteClassAttribute(Type concreteType) {
        _concreteType = concreteType;
    }

    public Type ConcreteType { get { return _concreteType; } }
}

Then, I changed ConcreteClassProvider to look for this attribute on a type when it needs to locate the concrete class. To use it, I need to declare both my custom provider and my new attribute on a class:

[TypeDescriptionProvider(typeof(GeneralConcreteClassProvider))]
[ConcreteClass(typeof(GeneralConcreteForm))]
abstract partial class GeneralAbstractForm : Form {

    public GeneralAbstractForm() {
        InitializeComponent();
    }

    public abstract string AbstractProperty { get; set; }
}

I now have a mechanism I can use for a large scale project.

Summary

While it is a bit of a drag that the Windows Forms designer doesn’t support abstract classes, by using this simple technique you can trick it into doing so. This requires at least Beta 1 of Visual Studio Whidbey. As I found when trying to use the inheritance picking dialog, there may still be pitfalls here, so before going hog wild with your own projects you should make sure this technique works for you in all the scenarios you care about. Microsoft doesn’t officially support designing abstract base classes, so as I said above, you will be on your own if something doesn't work for you.

You can download the project I used below.

AbstractBaseClass.zip (37.95 kb)

Comments (38) -

Joey Calisay, Thursday, February 24, 2005 at 1:06 AM

Finally, some new designtime stuff to learn from whidbey.  Thanks...

Jeff LeBert, Sunday, March 20, 2005 at 1:59 PM

This is so cool! I've got a couple places where I could use this for abstract base forms, but I'm more excited about using it for generic form/components. Generics cause the same problem as abstracts for the designers in Visual Studio. I want to build generic base forms/components that take a typed DataSet. Up until now, the only way I had of dealing with this was to build a template that has two classes in it. The first class would inherit off the generic base form/component and specify the typed DataSet to use. The second class would derive off of the first class and would be usable in the IDE since its base class isn't generic. This is SOOOOO ugly I had decided to give up on using abstract or generic base forms/components. Thanks for making my profession life a little better!

David M. Kean, Sunday, March 27, 2005 at 11:36 PM

I can't seem to get it to work.  Its still comes up with 'The designer must create an instance of type 'AbstractBaseClass.GeneralAbstractForm' but it cannot because the type is declared as abstract.', even with your GeneralDerivedForm.
I'm using Visual Studio 2005 (Version 8.0.41115.19). What do you think?

Frank Hileman, Thursday, March 31, 2005 at 9:33 AM

Is there effort being made to reduce the design-time overhead? We are creating a custom graphical designer SDK based on our existing root designer integrated in Visual Studio .NET. One problem with Visual Stuio is the per-element overhead, with the per-IComponent IDesigner, and all the property filtering. Since our users are creating vector graphics they often create hundreds of elements, and would like to create thousands, but that is not feasible with the Visual Studio design-time architecture.
The SDK we are building is specifically for external custom designers, so we thought we would make some changes to reduce the overhead, such as eliminating the per-element IDesigner, and not using code serialization and reflection for undo/redo. Also we would like to make the component changes within a designer transaction preserve the order of changes in undo/redo. Currently it does not and causes complexity.
Aside from overhead reduction, have you thought of putting design-time class bindings, such as Designer and UITypeEditor, in config files instead of in attributes? This way the design-time bindings could be changed without rebuilding the run-time. Putting all the design-time attributes in a config file would add great flexibility. We are thinking of putting many of the most common operations, that currently require a separate class or overridden methods, into a config file. For example, adding and removing properties from the property grid, on a per-class basis, is very common. Verbs could be added this way as well. Steven Clarke gave me this idea:
blogs.msdn.com/.../403436.aspx

Brian Pepin, Friday, April 1, 2005 at 7:16 AM

Frank --
You've touched upon some key areas we are thinking very hard about.  The per-element cost of IDesigner becomes apparant when your element count gets into the thousands.  Remember, IDesigner is just part of it; another part is the site and the container (which is implemented as a linear list of objects).  Also, the current model doesn't have a very scalable "opt-in".  You get it all, even if you don't necessarially need it.
Attributes are another thing we are definitely looking at.  Today, we must produce a new version of the .NET Framework with new metadata each time we want to add new features to the Windows Forms designer.  That doesn't allow us to be as agile as we would like.
Hopefully I'll be able to post more about our ideas in the next couple of months.

Brian Pepin, Friday, April 1, 2005 at 7:22 AM

David -
I have some bad news on this:  it doesn't work on Beta 2 bits.  There is a bug in the logic that sniffs for abstract classs in both Beta 1 and Beta 2 of VS, but the bug does not manifest itself in Beta 1.  I got the bug fixed for post-Beta 2 builds of VS so this will work eventually, it looks like it will be broken on Beta 2.  This is really unfortunate, because Beta 2 will be a really big release and is really solid.

Frank Hileman, Friday, April 1, 2005 at 7:32 AM

Yes, the Site and Container would be eliminated in our model. We have no need for the container, as we already have an object hierarchy with well defined boundries (Sub Pictures, the equivalent of UserControls, are the serialization boundry), and we can store Name properties other ways.
It is difficult to profile Visual Studio but the slow designer transaction creation, as well as undo/redo with hundreds of objects, seems primarily related to CodeDom and reflection. The property grid also has some inefficiency.
One primary goal for us is to allow the most common design-time customization tasks with little or no code.
Thanks for the feed back -- nice to see MS is having the same problems! I know some CodeDom and reflection speed issues are being addressed in Whidbey.

Ralf, Monday, November 28, 2005 at 10:12 AM

Hi,
maybe you can help me designing forms with MSVS2005 IDE which inherits from abstract class and are generic ?
I mean such a class :
public abstract class DialogForm<dialogTyp> : System.Windows.Forms.Form {}

Brian, Monday, November 28, 2005 at 6:14 PM

Ralf --
You should be able to do the same thing I describe.  That is, provide a stand-in class that implements the abstract base class.  Note that I haven't tried this, however.  I remember we did a lot of work in VS 2005 to keep the designer moderately happy when it encouters a generic type so I have no idea if there is some sticky part where my technique for creating a stand-in class will fall apart.

Ralf, Tuesday, December 13, 2005 at 1:36 PM

Hi Brian,
i made the following changes to your example in "content-downloads-AbstractBaseClass.zip"
in file "GeneralAbstractForm.cs":
abstract partial class GeneralAbstractForm : Form {
->
abstract partial class GeneralAbstractForm<typ> : Form {
internal class GeneralConcreteForm : GeneralAbstractForm
->
internal class GeneralConcreteForm : GeneralAbstractForm<int> {
in file "GeneralDerivedForm.cs":
internal class GeneralDerivedForm : AbstractBaseClass.GeneralAbstractForm {
->
internal class GeneralDerivedForm : AbstractBaseClass.GeneralAbstractForm<int> {
That are all changes to parametrize the abstract form. But the VS2005-Designer doesnt let me design the form. I tried now for a week and I have no more ideas.
I would be cool if it works....
Help ?
I think I have to change the "GeneralConcreteClassProvider". Does it have to be parametrized too ?

Damian, Monday, December 19, 2005 at 3:44 PM

Brian,
I too would love to get this working for generic base classes. Could you do another post on this topic in particular?

espinete, Tuesday, February 7, 2006 at 12:28 AM

Hello mister,
I would love to get design abstract form for VS 2003 and Framework .NET 1.1. Is it possible ?
Thanks and regards.

Andrés, Saturday, February 11, 2006 at 9:45 AM

Did anybody manage to get this working for generics?? I've been trying for a couple of hours and I still can't get it right...
Is it  possible to accomplish this?

Ralf, Tuesday, February 14, 2006 at 5:01 AM

Hi all,
someone might have the solution but I dont understand it:
kjellsj.blogspot.com/.../...-control-abstract.html
Hope somebody can post an example code.
Ralf

Ralf, Tuesday, February 14, 2006 at 7:18 AM

My previous comment was  wrong. I didnt understood the article.
Maybe next time I should look at the URL.
Sorry

Brian, Tuesday, February 14, 2006 at 5:01 PM

espinete -- No, it isn't possible to use this technique for VS 2003.  It requires new features in the 2.0 framework.  Sorry, Brian.

Andrés, Thursday, February 16, 2006 at 5:40 AM

Hi Brian! I managed to get this working for an abstract control using generics.
The thing is that apparently, the typedescruptor attribute gets carried forwart to the defiving classes, and so, when i add a derived control into a form it actually adds the "fake" control which the typedescruptor provides. How can I override that behavior? is it possible to set the default type descriptor for my derived class?

Andrés, Thursday, February 16, 2006 at 6:20 AM

Ok, so now i get it: In order to get things working right when using your derived control, just add a:
<TypeDescriptionProvider(GetType(TypeDescriptionProvider))>
or
[TypeDescriptionProvider(typeof(TypeDescriptionProvider))]
to your derived class so that when you add it to the vs toolbox the control will behave as expected!

Ralf, Tuesday, February 21, 2006 at 6:57 AM

hi,
could somebody post a full source code, so everyone can see how it works ?
Thanks

matt, Friday, March 31, 2006 at 5:29 AM

Surely a quicker way of doing this would be the following:
First, set the word DESIGNVIEW (for example) as a conditonal compiliation symbol (in project properties)
/// Update the class definition...
#if DESIGNVIEW
    internal class ctlMyControl : System.Windows.Forms.UserControl {
#else
    internal abstract class ctlMyControl : System.Windows.Forms.UserControl {
#endif
/// Update the method definition...
#if !DESIGNER
    internal abstract bool DoStuff();
#endif
/// ...finally update the method call in the derived class(es)
#if !DESIGNVIEW
    internal override bool DoStuff() {
        // Code here...
    }
#endif
The above will now allow the form to be rendered in the designer - before running, just change or delete the DESIGNVIEW text to allow the abstract methods to be called - obviously, put back the DESIGNVIEW if you wish to then continue in the designer.
Hope this is of help to someone...

xal, Wednesday, April 5, 2006 at 8:11 AM

Is there a way of making the controls in my base form available to the derived form in the designer?
Normally, when you inherit from a form in VS, you can click on the inherited buttons, labels, etc. and see their properties, and even change them if their modifiers allow you to (protected, protected friend, public).
With this approach, that seems to be disabled. You can see the controls, but you can't actually select them. Is there any way to overcome this?
Thanks.

Brian, Saturday, April 15, 2006 at 7:35 PM

xal --
The base class's controls should be editable if they were protected or public.  If they were internal or private you won't be able to change their properties.

xal, Monday, April 17, 2006 at 2:56 PM

Well, they're accesible through code, of course, but in the designer you normally can select inherited controls and change their properties in the designer, even move them around the form.
With this approach, you can see the inherited controls, but you can't click on them.
In your project, add a button to AbstractForm.cs with the designer, recompile and you'll see the button in DerivedForm.cs (at design time) but you can't click on it as you normally could in visual inheritance cases...

Paul, Tuesday, April 25, 2006 at 10:29 AM

Hi
Should this work for abstract UserControls too. I just spent quite a lot of time converting my Interface to an abstract class as I found I was writing  the same code in most of the methods... Maybe I should have done it on a smaller scale first - but who would have thought it...
Anyway I'm having trouble getting it to work so that VS will render the Inherited controls. I think I've followed your recipe correctly... Is there anything special about controls that would stop them working?
Thanks for posting what you have so far - at least I know what I'm up against.
Paul

Brian Pepin, Tuesday, April 25, 2006 at 10:13 PM

Paul --
Controls should work the same way.  With user controls, there are two "modes":  are you designing the control itself in its own designer, or are you having trouble with the control when it is placed on a form?  Designing the control should be identical to the form case.  Placing the control on a designer might go down some code path that doesn't work with this technique.
Xal --
Hm.  That's a bit of a bummer.  I think the reason why is that the inheritance scanner (the thing that scans all fields on the base class to look for members to show) is probably looking just at the base class, and not its base class.  If this is indeed what is going on there are ways to fix it, but it is tricky.  The simplest way might be the following:
1.  Override the Site property on your concrete form class.  
2.  When it gets sited by the designer, search in the form's base class for fields that derive from control and are non-null.
3.  For each field, call TypeDescriptor.AddAttributes to add an InheritanceAttribute with an appropriate InheritanceLevel.
4.  Also for reach field, add the instance to your site's container.
That basically does what the designer does to expose inherited controls.  You will only want to do this for controls you want to be able to modify -- for private fields the designer does a lot of additional metadata work to make all the properties read-only.  If you didn't do that, you could write to these properties which would then generate compile errors because you can't actually get to the fields in code.

xal, Thursday, May 11, 2006 at 10:16 AM

Brian,
This is what I've come up with with the instructions you gave me.
    Public Overrides Property Site() As System.ComponentModel.ISite
            Get
                Return MyBase.Site
            End Get
            Set(ByVal value As System.ComponentModel.ISite)
                MyBase.Site = value
                If Site.DesignMode Then
                    Dim t As Type = Me.GetType.BaseType
                    Dim ctrlType As Type = GetType(Control)
                    Dim bindFlags As Reflection.BindingFlags
                    bindFlags = Reflection.BindingFlags.NonPublic _
                        Or Reflection.BindingFlags.Public _
                        Or Reflection.BindingFlags.Instance _
                        Or Reflection.BindingFlags.Static _
                        Or Reflection.BindingFlags.FlattenHierarchy
                    For Each fi As FieldInfo In t.GetFields(bindFlags)
                        If fi.IsPublic Or fi.IsFamily Then
                            If fi.FieldType.IsSubclassOf(ctrlType) Then
                                Dim ctl As Control
                                ctl = CType(fi.GetValue(Me), Control)
                                If ctl IsNot Nothing Then
                                    Dim attInherit As New InheritanceAttribute(InheritanceLevel.Inherited)
                                    TypeDescriptor.AddAttributes(ctl, New Attribute() {attInherit})
                                    Site.Container.Add(ctl)
                                End If
                            End If
                        End If
                    Next
                End If
            End Set
        End Property
I don't know if this is exactly what you instucted me to do, but as it is, it fails.
When i add the instance of the field to the site's container, it makes the whole form open outside visual studio as if I where running the app, and when I click on that form i get an exception.
I'm also not sure what the appropiate inheritance level should be, but i tried all and failed.
Is there any more info you can throw in the mix?
Thank you very much for your help

xal, Thursday, May 11, 2006 at 10:43 AM

Brian, Sorry, I added this check:
If fi.IsPublic Or fi.IsFamily Then
Just before posting... It seems that protected members are not returned as public or family and the problems I told you about don't appear...
So, in order to see what I'm talking about you'll need to remove that check.

xal, Friday, May 12, 2006 at 11:35 AM

Ok, I figured it out.
You can't do it inside the property set because it crashes. I'm doing it on form load (adding a handler in the concrete class provider for forms / user controls).
One thing that was particularly confusing whas that I didn't have to check fields, but properties.
Controls are added to the form as fields but when compiled, they're actually properties, while the field exists as private with an underscore as prefix.
So Button1 would have a _Button1 field that is private and a property named Button1.
  
So, since it's a property, you have to check it's accesors to see if it really is public or protected.
  
One funny thing is that private controls (which I mark as InheritanceLevel.InheritedReadOnly) can be moved / resized in the designer, but when you close/reopen the designer it restores to it's original state.
Another issue that I had is that I can't seem to find a way to support friend properties. Since there's no obvious way of telling who's actually inheriting from the control, you can't say whether the assembly is the same or not. (I tried GetCallingAssembly, but it's always either itself or System.Windows.Forms)
Thanks for all your help!!

Brian, Thursday, June 29, 2006 at 10:59 PM

Xal --
Glad you got this working.  You must be using VB.  VB creates properties instead of fields so it can wire the internal plumbing of the "handles" clauses into the property acccessors.  VB will add a special attribute to each field it maps to a property called the AccessedThroughPropertyAttribute.  The Windows Forms designer checks for this attribute when it looks for inherited fields so it can find the actual identifiers.
Thanks,
Brian.

Xal, Tuesday, July 18, 2006 at 7:21 AM

FWIW, here's a link to my implementation:
http://forums.lhotka.net/forums/thread/336.aspx
I hope it helps!!

Redwolf, Monday, September 4, 2006 at 6:40 AM

How do i add this funktionality in NETCF there is no TypeDescriptionProvider

Brian, Sunday, September 10, 2006 at 4:45 AM

Redwolf --
I don't believe you can.  At design time, netcf uses the desktop framework to run the designer so TypeDescriptonProvider is available, but since you have to compile this into your code your compile will fail since you bind to the netcf libraries to compile.

Ken, Wednesday, September 20, 2006 at 6:52 AM

I was trying this out with UserControls.
I noticed it works for one level of inheritance ( UC2 : UC1), UC2 will be shown in the designer.
But adding another level ( UC2 : UC1  &  UC1 : UCbase) results in the designer for UC2 saying  "The designer must create an instance of type 'WindowsApplication1.UserControl1' but it cannot because the type is declared as abstract. ".
It had been perfectly happy with UC1 being abstract before another inheritance level was added below UC1.  
Any thoughts on this?

Brian, Friday, September 22, 2006 at 4:06 PM

Ken --
Yes, I believe you're hitting this code:
    if (objectType == typeof(AbstractForm)) {
        objectType = typeof(ConcreteForm);
    }
Here, I only create an instance of the concrete form if the type passed in is exactly the abstract form.  You would need to add a ConcreteTypeAttribute to UC1 to make this work.
Another possibility is to change the above code so that it respects sub types of the abstract type.  I would only wan to do this if the subtype was abstract too:
    if (objectType.IsAbstract && typeof(AbstractForm).IsAssignableFrom(objectType)) {
        objectType = typeof(ConcreteForm);
    }
I haven't tried that, but it should work.

Ken, Monday, September 25, 2006 at 8:33 AM

Thanks Brian,
That did solve the problem I was seeing.
For VS2005,  I did notice that if there are any errors when the designer attempts to display the class, just fixing the error does not usually allow the designer to start working.   Usually VS2005 needs to be shutdown and relaunched.  Just an FYI for anyone whose design page shows an error no matter what they do.

Arnaud Weil, Friday, November 17, 2006 at 2:53 PM

Now, this is something I've been looking for during a long time. Tanks so much for sharing this tip!

Michael Morrison, Monday, December 28, 2009 at 12:46 PM

I know it's been a very long time since the last comment, so hopefully you'll get this.  I've downloaded your example and it works in my copy of VS2005, however, recreating the example in VC# Express 2008, the error still comes up.  Is this an Express issue, or does your example only work in VS2005 and not in VS2008 of any PID?

Brian, Monday, December 28, 2009 at 3:24 PM

Mike -- I don't believe there are any differences in the express or standard versions of the designer that ships with VS 2008.  I have't worked on the winforms designer for a few years and I don't know what they changed in 2008.  As far as I know, the technique should still work fine.
Comments are closed