Unleashing the wonders of the Component model
One of the things that made me like the .Net framework so much was the component model, along with the design time support.
At first sight, a Component looks like a stand alone piece of code you can parametrize and use in your own code. The easiest way to do so is to create a component derived class and to drop components on it. Then, the design time mechanism quicks in and you can use the PropertyGrid to set the properties and register the event listeners.
When you look closer, you begin to notice that the behavior of the Component can be modified by the Container in which it is added. The Site, which bind the Component to the Container, is a service provider. Therefore, when the Component requires services, it asks its Site. This mechasnism is called Inversion of Control.
Implementing a Container that support adding services
The documentation states that the Container allows the Components to interact with themselves and with their environment. Unfortunately, the designer instanciate a basic Container to store the component. Such implementation does not provide a lot of services, especially not a service container to allow components to register new services that can be used by other components.
Nevertheless, it is not very difficult to implement:
public class ServingNestedContainer : NestedContainer, IServiceProvider
{
#region WrappedServiceProvider Class
class WrappedServiceProvider : IServiceProvider
{
IComponent component;
public WrappedServiceProvider(IComponent component) {
this.component = component;
}
public object GetService(Type serviceType) {
if (serviceType.IsAssignableFrom(component.GetType())) {
return component;
}
if (component.Site != null) {
return component.Site.GetService(serviceType);
}
return null;
}
}
#endregion
public ServingNestedContainer(IComponent owner)
: base(owner) {
serviceContainer = new ServiceContainer(new WrappedServiceProvider(owner));
}
public IServiceContainer ServiceContainer {
get {
return serviceContainer;
}
}
IServiceContainer serviceContainer;
#region IServiceProvider Members
protected override object GetService(System.Type service) {
object serviceInstance = base.GetService(service);
if (serviceInstance != null)
return serviceInstance;
serviceInstance = serviceContainer.GetService(service);
return serviceInstance;
}
object IServiceProvider.GetService(Type serviceType) {
return GetService(serviceType);
}
#endregion
}
This container is meant to be used only in a IComponent, so that services can be provided by the owner. So, to use it on a component, just add the following code in the constructor:
ServingNestedContainer container = new ServingNestedContainer(this);
if(components != null && components.Components.Count > 0) {
IComponent[] collection = new IComponent[components.Components.Count];
components.Components.CopyTo(collection, 0);
foreach (IComponent component in collection) {
components.Remove(component);
container.Add(component);
}
}
components = container;
In design time, components that have a contructor accepting a IContainer as parameter are automatically added to the components field.
Sample use case
So now you can request the IServiceContainer on the ServingNestedContainer, and add services to it.
For instance, you can provide implementations of IUIService and ISelectionService to your UserControls so they can interact with the user in a sandardize manner.