DekGenius.com
[ Team LiB ] Previous Section Next Section

9.1 Design Time Versus Runtime

There are two contexts in which controls have to operate: design time and runtime. Design time refers to when the control is being displayed in the Forms Designer. Runtime simply means normal execution of a program that uses the control.

Controls do not need to provide any explicit support to allow the Forms Designer to host them. As we saw in Chapter 5, even a very simple custom control derived directly from System.Windows.Forms.Control with nothing more than an override of the OnPaint method added can be dropped onto a form in the Designer. Visual Studio .NET just creates an instance of the control to render it in the Designer. It uses the normal control painting mechanisms to do this, so controls do not need any special code to make this work. Selection outlines and resize handles are added by the Forms Designer after the control has drawn itself.

The Forms Designer intercepts all mouse and keyboard input. This means that all controls automatically get the standard editing facilities, such as drag-and-drop resizing and positioning and double-clicking to add a default event handler. So again, no special code is required to make a control behave properly at design time.

9.1.1 Detecting Design Time

Many controls will not need special code to be useful at design time. However, certain controls may need to change their behavior in order to work correctly in the Forms Designer. They will therefore need to detect whether they are operating in a runtime or a design-time environment and behave accordingly.

For example, suppose you wrote a control that connected to a web service and provided live information from that service. (For example, your control might be a stock price monitor.) You might not want the component to connect to the real service at design time if that service is slow or only sporadically available during the development phase of your project. It would be irritating to have to wait for the control to try to connect to the service every time you use it in the Forms Designer. At design time, you typically won't care about the correctness of the displayed data; you merely want to make sure the layout of your form is correct. (Data-bound controls usually behave differently at design time and runtime for this reason.) Fortunately, it is trivial to determine whether your control is being hosted in the designer or is running for real—simply test the Control class's DesignMode property, as shown in Example 9-1.

The Control class inherits DesignMode from its base class, Component, so all components can detect when they are being used at design time, not just controls.


Example 9-1. Modifying a control's design-time appearance with DesignMode
// C# code
protected override void OnPaint(PaintEventArgs pe)
{
    pe.Graphics.DrawString(DesignMode ? "Design" : "Runtime",
        Font, Brushes.Black, ClientRectangle);

    base.OnPaint(pe);
}
' VB Code
Protected Overrides Sub OnPaint(pe As PaintEventArgs)
    pe.Graphics.DrawString(IIf(DesignMode, "Design", "Runtime"),
        Font, Brushes.Black, ClientRectangle)

    MyBase.OnPaint(pe)
End Sub

There is one caveat with the DesignMode property: you should not use it in a constructor. The control's DesignMode property is simply a shortcut for the ISite.DesignMode property of the component's Site property, and Site will always be null or Nothing during construction. The Site property is used to provide components with a means of communicating with their containing environment, enabling us to integrate our controls with certain Visual Studio .NET design-time features. The Forms Designer sets all components' Site properties at design time. (Visual Studio .NET 2003 supplies an object of type Microsoft.VisualStudio.Designer.Host.DesignSite for the Site. This object's ISite.DesignMode property always returns true.)

The reason DesignMode cannot be used during construction is that the Forms Designer must create an instance of our control before it can set the Site property. This means that the Site property will not be set until after the constructor completes. Consequently, the control's DesignMode property will not be able to ask the Site for its ISite.DesignMode property, and so it always returns its default value of false during construction.

There is a way to obtain a Site during construction, but it turns out not to solve this particular problem. The Forms Designer recognizes an alternate constructor that takes an IContainer as a parameter. If the constructor calls Add(this) or Add(Me) on the supplied container, the container will set the Site property, making it available for the rest of the constructor:

public MyControl(IContainer c)
{
    c.Add(this);
    // Site property now valid...
}

Unfortunately, although the Forms Designer will generate code that uses this constructor at runtime in the InitializeComponent method, at design time it insists on using the parameterless constructor. So there is no way of accessing the Site during the constructor at design time, even if you supply this extra constructor.

This alternate constructor is only used to provide the component with a reference to the containing form's components member. This ensures that the component's Dispose method will be called automatically by the form's Dispose method, as described in Chapter 3.


Fortunately, Windows Forms provides a two-phase initialization style for controls. If your control implements the ISupportInitialize interface defined in the System.ComponentModel namespace, it will get an opportunity to run initialization code after the Site property has been set. In fact, it gets two opportunities—ISupportInitialize defines two methods, BeginInit and EndInit. Examples Example 9-2 and Example 9-3 illustrate the use of these methods.

Example 9-2. Detecting design-time initialization with ISupportInitialize using C#
public class MyControl :
  System.Windows.Forms.Control,
  System.ComponentModel.ISupportInitialize
{
    public void BeginInit()
    {
        Debug.WriteLine(DesignMode);
    }

    public void EndInit()
    {
        Debug.WriteLine(DesignMode);
    }
    . . .
}
Example 9-3. Detecting design-time initialization with ISupportInitialize using VB
Public Class MyControl
   Inherits System.Windows.Forms.Control
   Implements System.ComponentModel.ISupportInitialize

   Public Sub BeginInit() Implements ISupportInitialize.BeginInit
      Debug.WriteLine(DesignMode)
   End Sub

   Public Sub EndInit() Implements ISupportInitialize.EndInit
      Debug.WriteLine(DesignMode)
   End Sub
   . . .
End Class

The Forms Designer will set the Site property before calling either BeginInit or EndInit, so in Example 9-2, the DesignMode property will be true in both methods at design time. The difference between these two methods is that BeginInit will be called before any of the component's properties are set, and EndInit will be called after all properties have been set.

Armed with the knowledge of which kind of environment they are running in, many components can integrate successfully with the development environment by simply adjusting their behavior and appearance suitably at design time. They can also modify certain aspects of the way they are presented using the attributes discussed in Chapter 5 and Chapter 8. However, some controls will want to go further than this—they may want to modify the behavior of the Forms Designer as well as their own behavior. So we will now look at how to write a control that customizes Visual Studio .NET's behavior.

    [ Team LiB ] Previous Section Next Section