Tim Van Wassenhove

Passionate geek, interested in Technology. Proud father of two

21 Aug 2007

Bending the code generation of IExtenderProvider to your will

In Exploring CodeDomSerializer i already explained how we can modify the code that the Visual Studio designer generates for us. With a typical IExtenderProvider the designer generates an initializer, SetXXX methods and a variable declaration, which looks like

this.constantsExtenderProvider1 = new WindowsApplication1.ConstantsExtenderProvider();

this.constantsExtenderProvider1.SetConstants(this.button1, new string[] {
"Operation1",
"Operation5"});

this.constantsExtenderProvider1.SetConstants(this, null);

private ConstantsExtenderProvider constantsExtenderProvider1;

Now, what if we’re not happy with those generated SetXXX methods on each Component? The problem is that this code is not generated by the serializer for the ConstantsExtenderProvider but by the serializers for the Components. An easy workaround for this problem is to set the DesignerSerializationVisibilityAttribute on the GetXXX method in our IExtenderProvider to Hidden.

With those ugly SetXXX methods out of the way it’s up to us to do it better. We do this by implementing a custom serializer for our ConstantsExtenderProvider

class ConstantsSerializer<T> : CodeDomSerializer
{
	public override object Serialize(IDesignerSerializationManager manager, object value)
	{
		ConstantsExtenderProvider provider = value as ConstantsExtenderProvider;

		CodeDomSerializer baseClassSerializer = manager.GetSerializer(typeof(ConstantsExtenderProvider).BaseType, typeof(CodeDomSerializer)) as CodeDomSerializer;
		CodeStatementCollection statements = baseClassSerializer.Serialize(manager, value) as CodeStatementCollection;

		IDesignerHost host = (IDesignerHost)manager.GetService(typeof(IDesignerHost));
		ComponentCollection components = host.Container.Components;
		this.SerializeExtender(manager, provider, components, statements);

		return statements;
	}

	private void SerializeExtender(IDesignerSerializationManager manager, ConstantsExtenderProvider provider, ComponentCollection components, CodeStatementCollection statements)
	{
		foreach (IComponent component in components)
		{
			Control control = component as Control;
			if (control != null && (control as Form == null))
			{
				CodeMethodInvokeExpression methodcall = new CodeMethodInvokeExpression(base.SerializeToExpression(manager, provider), "SetConstants");
				methodcall.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), control.Name));

				string[] constants = provider.GetConstants(control);
				if (constants != null)
				{
					StringBuilder sb = new StringBuilder();
					sb.Append("new string[] { ");

					foreach (string constant in constants)
					{
						sb.Append(typeof(T).FullName);
						sb.Append(".");
						sb.Append(constant);
						sb.Append(", ");
					}

					sb.Remove(sb.Length -- 2, 2);
					sb.Append(" }");

					methodcall.Parameters.Add(new CodeSnippetExpression(sb.ToString()));
				}
				else
				{
					methodcall.Parameters.Add(new CodePrimitiveExpression(null));
				}

				statements.Add(methodcall);
			}
		}
	}
}

And now the generated code looks like

this.constantsExtenderProvider1.SetConstants(this.button1, new string[] { WindowsApplication1.Constants.Operation1, WindowsApplication1.Constants.Operation5 });

As always, feel free to download the ConstantsExtenderProvider source.