Adding Design Time support to the ServingNestedContainer
The ServingNestedContainer is fine but it does not support DesignTime. Therefore, if you want to still be able to benefit from the productivity improvement of the designer, you have to re-site your components.
Fortunately, it is possible to implement a component that would, through its Designer, register a CodeDomProvider that overrides the CodeDomProvider in charge of initializing of the components field.
public class ServiceContainerComponentDesigner : ComponentDesigner
{
IDesignerHost host;
IDesignerSerializationManager manager;
IDesignerSerializationProvider provider;
public ServiceContainerComponentDesigner() {
}
public override void Initialize(IComponent component) {
base.Initialize(component);
// Obtain an IDesignerHost service from the design environment.
host = (IDesignerHost)component.Site.GetService(typeof(IDesignerHost));
if (host == null)
return;
manager = host.GetService(typeof(IDesignerSerializationManager))
as IDesignerSerializationManager;
if (manager == null)
return;
provider = new ContainerCodeDomProvider();
manager.AddSerializationProvider(provider);
}
protected override void Dispose(bool disposing) {
if (host != null && manager != null && provider != null) {
manager.RemoveSerializationProvider(provider);
}
base.Dispose(disposing);
}
}
The ContainerCodeDomProvider implementation returns a CodeDomSerializer that serializes the instanciation of the ServingNestedContainer instead of the traditional Container when a serializer is requested for IContainer.
internal class ContainerCodeDomProvider : CodeDomSerializer,
IDesignerSerializationProvider
{
#region IDesignerSerializationProvider Members
public object GetSerializer(IDesignerSerializationManager manager,
object currentSerializer,
Type objectType,
Type serializerType) {
if (typeof(IContainer).IsAssignableFrom(objectType)) {
return ContainerCodeDomProvider.Default;
}
return null;
}
#endregion
private static CodeDomSerializer defaultSerializer;
private const string containerName = "components";
private Type typeToProvide;
internal static CodeDomSerializer Default {
get {
if (defaultSerializer == null) {
defaultSerializer = new ContainerCodeDomProvider();
}
return defaultSerializer;
}
}
public ContainerCodeDomProvider() {
this.typeToProvide = typeof(ServingNestedContainer);
}
protected override object DeserializeInstance(IDesignerSerializationManager manager,
Type type,
object[] parameters,
string name,
bool addToContainer) {
if (typeof(IContainer).IsAssignableFrom(type)) {
object service = manager.GetService(typeof(IContainer));
if (service != null) {
manager.SetName(service, name);
return service;
}
}
return base.DeserializeInstance(manager, type, parameters, name, addToContainer);
}
public override object Serialize(IDesignerSerializationManager manager,
object value) {
CodeExpression codeExpression;
CodeTypeDeclaration codeTypeDeclaration
= manager.Context[typeof(CodeTypeDeclaration)] as CodeTypeDeclaration;
RootContext rootContext = manager.Context[typeof(RootContext)] as RootContext;
CodeStatementCollection statements = new CodeStatementCollection();
if ((codeTypeDeclaration != null) && (rootContext != null)) {
CodeMemberField componentsField
= new CodeMemberField(typeof(IContainer), containerName);
componentsField.Attributes = MemberAttributes.Private;
codeTypeDeclaration.Members.Add(componentsField);
codeExpression
= new CodeFieldReferenceExpression(rootContext.Expression,
containerName);
} else {
statements.Add(
new CodeVariableDeclarationStatement(typeof(IContainer),
containerName));
codeExpression = new CodeVariableReferenceExpression(containerName);
}
base.SetExpression(manager, value, codeExpression);
// new 'typeToProvide'(this);
CodeObjectCreateExpression containerExpression
= new CodeObjectCreateExpression(typeToProvide,
new CodeExpression[] {
new CodeThisReferenceExpression()
});
CodeAssignStatement statement
= new CodeAssignStatement(codeExpression, containerExpression);
statement.UserData["IContainer"] = "IContainer";
statements.Add(statement);
return statements;
}
}