In VS05, when a component is dragged onto a Windows Forms design surface, it is instantiated by the Windows Forms Designer, which also grabs a reference to its IComponent interface. At the highest level, the Windows Forms Designer manages a form's UI and code views; the Windows Forms Designer's internal IDesignerHost (from the System.ComponentModel.Design namespace) implementation, the designer host, has the specific responsibility of managing design-time objects and providing design-time services. The designer host stores IComponent references to all design-time objects hosted on a form, including the form itself; as Figure 11.1 shows, forms are also components because they indirectly implement IComponent. The resulting collection of design-time objects is available from the IDesignerHost interface through the Container property, which is of type System.ComponentModel.IContainer (from the System.ComponentModel namespace): namespace System.ComponentModel { interface IContainer : IDisposable { ComponentCollection Components { get; } void Add(IComponent component); void Add(IComponent component, string name); void Remove(IComponent component); } } Implementing IContainer provides the mechanism by which the designer host can access and manage components. Similarly, contained components can access the designer host and each other through their container at design time. Figure 11.2 illustrates this two-way relationship. Figure 11.2. Design-Time Architecture (See Plate 15)Figure 11.2 shows that the relationship between a component and its designer host is established via the ISite interface (from the System.ComponentModel namespace): namespace System.ComponentModel { interface ISite : IServiceProvider { IComponent Component { get; } IContainer Container { get; } bool DesignMode { get; } string Name { get; set; } } } Internally, a container stores a list of sites. When each component is added to the container, the designer host creates a new site and connects the component to it by setting the latter's IComponent.Site property implementation to the new site's ISite reference.[2] By establishing this relationship, IComponent implementations acquire direct access to their site. Additionally, the Component class provides direct access to the container, and this avoids the complexity of navigating to it via the ISite reference:
namespace System.ComponentModel { class Component : MarshalByRefObject, IComponent, IDisposable { ... public IContainer Container { get; } // Direct container access public virtual ISite Site { get; set; } protected bool DesignMode { get; } protected EventHandlerList Events { get; } public event EventHandler Disposed; ... } } A component can also access the VS05 designer host itself by requesting the IDesignerHost interface from the container: IDesignerHost designerHost = this.Container as IDesignerHost; In VS05, the Windows Forms Designer has its own implementation of the IDesignerHost interface, although to fit into other designer hosts, it's best for a component to rely only on the interface and not on any specific implementation. To demonstrate the various services provided by the VS05 designer host to components, let's build on the AlarmClockControl created in Chapter 10: Controls: // AlarmClockControl.cs partial class AlarmClockControl : ScrollableControl { // Properties public DateTime Alarm { get; set; } // Methods public DateTime DelayAlarm(double minutes); // Events public event AlarmSoundedEventHandler AlarmSounded; } Figure 11.3 shows the control in action. Figure 11.3. AlarmClockControl in Action, Albeit at Design Time
While there's a strange satisfaction in knowing that AlarmClockControl works at design time as well as run time, such behavior should occur only at run time. To track down why and where this is occurring, we need to perform a little debugging. |