Building Windows Forms Controls and Components with Rich Design-Time Features
Michael Weinhardt and Chris Sells
Design-time Architecture
Design mode is activated the moment a form is opened for editing. It can involve activities like resizing, setting the border style, changing the caption, and adding controls and components, which also enter design mode the moment they are dragged onto either the visual or nonvisual design surface. The nonvisual area, or component tray, hosts components. A component is a Framework class that supports the design-time experience but doesn't draw its own UI in a container-specified region. Components implement the System.ComponentModel.IComponent interface, typically by deriving from the SystemComponent.Model.Component base class. Controls, on the other hand, do draw themselves and are therefore shown in the visual area. All controls ultimately derive from the System.Windows.Forms.Control class, which in turn derives from the Component base class. In other words, a control is really a component with a UI. Both act nearly identically at design time in terms of their integration with the design host. Figure 1 shows a form in design mode acting as a design host for a number of components and controls.
While both components and controls ultimately derive from the Component base class, that's not what distinguishes a component from any other class. Instead, what makes the Component class fit into the design-time architecture is its implementation of the IComponent interface. IComponent allows components to integrate with the design-time host, providing them with design-time services.
设计时架构
一旦窗体被打开编辑,设计模式就被激活了。它可以接受很多行为,例如,调整大小、设置边框风格,改变标题,同时也包括添加control(控件)和Component(组件),当它们被拖拽到可视或非可视区域时,也进入了设计模式。非可视区域,或者称为组件托盘,用来存放组件。组件是一个Framework类,它支持设计时体验,但是它不在container(容器)指定的区域绘制自己的UI(用户界面)。组件实现System.ComponentModel.IComponent接口,更典型的,它继承自基类System.ComponentModel.Component。另一方面,控件却(do)绘制自己,因此它们可以显示在可视区域。所有控件最终都继承自System.Windows.Forms.Control类,而后者又继承自Component基类。换句话说,控件实际上是带有UI的组件。在设计时期间,二者与设计宿主结合的方式几乎完全一致。图1显示了一个在设计模式下做为许多组件和控件的设计宿主的窗体。
既然组件和控件都最终继承自Component基类,因此组件与其它类没有什么区别。Instead,使Component类适合设计时架构的是它对IComponent接口的实现。IComponent允许组件与设计时宿主结合,并为它们提供设计时服务。
Hosts and Containers
In Visual Studio® .NET, the Windows Forms Designer is responsible for providing design-time services during Windows Forms development, including a form's UI and code views. The responsibility of managing integration between design-time objects and the designer is handled by an internal implementation of System.ComponentModel.Design.IDesignerHost. The designer host stores IComponent references to all design-time objects on the current form as well as the form itself, which is also a component. This collection of components is available from the IDesignerHost interface through the property of type System.ComponentModel.IContainer:
This implementation of IContainer allows the designer host to establish a relationship that helps it manage each of the components placed on the form.
宿主和容器
在Visual Studio® .NET中,Windows窗体设计器负责在Windows 窗体开发期间提供设计时服务,包括窗体UI和代码视图。管理设计时对象和设计器集成的任务由一个System.ComponentModel.Design.IDesignerHost接口的内部实现处理。设计器宿主储存对当前窗口中所有设计时对象的IComponent引用,包括窗口本身,因为它也是一个组件。该组件集合可以通过IDesignerHost接口的Container属性访问,该属性的类型为System.ComponentModel.IContainer:
interface
IContainer : IDisposable
{
ComponentCollection Components { get; }
void Add(IComponent component);
void Add(IComponent component, string
name);
void Remove(IComponent component);
}
对IContainer接口的这一实现允许设计器宿主建立关系,从而帮助它管理放在窗体上每一个组件。
Sites
At design time, contained components can access the designer host, as well as each other, through their container. This two-way relationship is shown in Figure 2. You can also see that the fundamental relationship between the designer host and its components is established with an implementation of the System.ComponentModel.ISite interface:
在设计时,被包含的组件和设计器宿主可以通过它们之间的容器互相访问。这一双向的关系显示在图2中。可以看到,设计器宿主和其组件的基础关系是通过实现一个System.ComponentModel.ISite接口建立的:
interface
ISite : IServiceProvider
{
IComponent Component { get; }
IContainer Container { get; }
bool DesignMode { get; }
string Name { get; set; }
}
Internally, a container stores an array of sites. When each component is added to the container, the designer host creates a new site, connecting the component to its design-time container and vice versa, by caching the ISite interface in the IComponent.Site property implementation.
在内部,一个容器存储一个site数组。每当组件被添加到容器时,设计器宿主就创建一个新的site,并通过缓存IComponent.Site属性中实现的ISite接口,连接组件和其设计时容器,反之亦然。
interface
IComponent : IDisposable
{
ISite Site { get; set; }
event EventHandler Disposed;
}
The Component base class implements IComponent and caches the site's interface in a property. It also provides a helper property to go directly to the component's container without having to go through the site first:
组件基类实现IComponent并在属性中缓存site的接口。它同时提供一个辅助属性直接访问组件容器,而不必首先通过site:
class
Component : MarshalByRefObject, IComponent, IDisposable
{
public IContainer Container { get; }
public ISite Site { virtual get; virtual set; }
}
The Component base class provides a component with access to both the container and the site directly. A component can also access the Visual Studio .NET designer host by requesting the IDesignerHost interface from the container:
组件基类提供一个组件,通过它可以直接访问容器和site。组件也可以通过请求容器的IDesignerHost接口访问Visual Studio .NET的设计器宿主。
IDesignerHost designerHost
=
this
.Container
as
IDesignerHost;
In Visual Studio .NET, the designer has its own implementation of the IDesignerHost interface, but to fit into other designer hosts, it's best for a component to rely only on the interface and not any specific implementation.
在Visual Studio .NET中,设计器有它自己对IDesignerHost接口的实现,不过为了适应其它的设计器宿主,组件最好只依赖接口而不应依赖于其特定的实现。