Roland Weigelt

Born to Code

News

.NET related Links

Archives

When your WinForms UserControl drives you nuts

WinForms user controls are that kind of technology that seems to be very easy at first, works pretty well for quite a while and then BAM! explodes in your face. I spent some hours today solving problems with a control that was working fine in the running application, but just didn't like the IDE's designer anymore, throwing exceptions all over the place. Here are some tips based on what I learned today searching Google's Usenet archive:

1. Don't take things for granted
Just because you can create a simple user control (e.g. combining a text field and a buttons) without reading any documentation, doesn't mean that knowledge about what is actually happening in design mode isn't necessary. Always keep in mind that there's some code running even though your program is not started. When a form or a control is displayed in the IDE in designer view, its (default) constructor is called which in turn calls InitializeComponent(). And this is where the trouble usually starts when this form/control contains a badly written user control.

2. Take care of your control's public properties
If you don't add any attributes to a read/write property (huh, what attributes?), two things happen:

  • The property is shown in the "properties" window in the IDE.
  • The initial value of the property is explicitly set in the InitializeComponent() method.

Hiding properties that don't make sense to be edited at design time is a good idea even if you don't intend to write a commercial-quality, bullet-proof control ready to be shipped to thousands of developers. It's simply a matter of good style and it's done with very little effort using the Browsable attribute:

[ Browsable(false) ]
public string MyProperty
{
...
}

The second point can be quite a shocker in some cases, because it means that

  • the property is read when the control is placed on a form (quick experiment: throw an exception in the "getter" and see what happens in the designer).
  • the property is set at the time the control is created, not when it is actually added to the form.

As long as your control only gets or sets the value of a private member variable, things are fine. But if e.g. reading the property triggers some actions that require the control to be completely initialized, you can get into trouble (remember the "Add any initialization after the InitializeComponent call" comment in the constructor?). And setting the property "too early" can be a problem e.g. if your property's setter contains code that performs some kind of automatic update.

This behavior of the designer can be avoided by using the DefaultValue attribute:

private string m_strMyText="";
...
[ DefaultValue("") ]
public string MyText
{
...
}

The MyText property will not be set to "" in the InitializeComponent() method (which would be the case without the attribute).

While you're at it, consider adding the Description and Category attributes as well. Adds a nice touch.

3. Don't rely on this.DesignMode too much
The inherited DesignMode property of your user control is an easy way to find out whether the control's code is running in the designer. Two problems:

  • You can't use it in the control's constructor. At that point, the control's "Site" (which provides the actual value of DesignMode) hasn't been set, thus DesignMode is always false in the constructor.
  • "Running in the designer" is a matter of point of view:
    • When editing a control MyControl in the designer, MyControl is obviously in "design mode".
    • When using MyControl on a form being edited in the designer, MyControl is still in "design mode".
    • But if MyControl is placed on MyOtherControl, and MyOtherControl is placed on a form, MyControl is no longer in design mode (but MyOtherControl still is)
    Strange? Not really, if you think about it (if... I didn't)

4. If you just don't know what to do: debug!
Open the project's properties dialog, set the debug mode to "program", apply, set "start application" to your VS.Net IDE ("devenv.exe" in the "Common7\IDE" directory). Set some breakpoints in your code (e.g. in property getters/setters) and start debugging. A new IDE will open (empty), you load the project and then open the control in designer view. Cool stuff.

Comments

No Comments