Exploring CodeDomSerializer

Sometimes we want absolute control over the code that the visual studio designer generates. Imagine that we have a UserControl with a Number property and instead of the default “this.userControl1.Number = 27;” code that the designer would generate we want it like “this.userControl1.Number = 1 + 3 + 23″. In order to achieve this we first have to inform the designer that we want custom serialization. This is done by adding a DesignerSerializerAttribute to our UserControl:

[DesignerSerializer(typeof(PrimeSerializer), typeof(CodeDomSerializer))]
public partial class UserControl1 : UserControl
{
 private int number;

 public int Number
 {
  get { return this.number; }
  set { this.number = value; }
 }

 // ...
}

And now it’s time to implement the PrimeSerializer for the custom assignment code:

public class PrimeSerializer : CodeDomSerializer
{
 public override object Serialize(IDesignerSerializationManager manager, object value)
 {
  UserControl1 uc = value as UserControl1;
  if (uc == null) { return null; }

  // Instead of implementing all the serialization code, we'll rely on the implementation of the baseclass, namely UserControl
  CodeDomSerializer baseClassSerializer = manager.GetSerializer(typeof(UserControl1).BaseType, typeof(CodeDomSerializer)) as CodeDomSerializer;
  Object codeObject = baseClassSerializer.Serialize(manager, value);

  // The only thing we have to do is find the statement where the assigment to the Number property is made, and replace that...
  CodeStatementCollection codeStatements = codeObject as CodeStatementCollection;
  CodeAssignStatement numberAssignmentStatement = this.FindNumberCodeStatement(codeStatements) as CodeAssignStatement;
  numberAssignmentStatement.Right = new CodeSnippetExpression(GetNumberAsSumOfPrimes(uc.Number));

  return codeObject;
 }

 private CodeStatement FindNumberCodeStatement(CodeStatementCollection codeStatements)
 {
  foreach (CodeStatement codeStatement in codeStatements)
  {
   CodeAssignStatement codeAssignment = codeStatement as CodeAssignStatement;
   if (codeAssignment != null)
   {
    CodePropertyReferenceExpression left = codeAssignment.Left as CodePropertyReferenceExpression;
    if (left != null && left.PropertyName == "Number")
    {
     return codeStatement;
    }
   }
  }

  throw new Exception("The CodeStatement for Number was not found");
 }

 private static string GetNumberAsSumOfPrimes(int number)
 {
  StringBuilder sb = new StringBuilder();

  int[] primes = new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101 };
  for (int i = primes.Length - 1; i >= 0 && number > 0; --i)
  {
   if (primes[i] <= number)
   {
    sb.Insert(0, primes[i]);
    sb.Insert(0, " + ");
    number -= primes[i];
   }
  }

  return sb.ToString().Substring(3);
 }
}

And now we can look at the generated code in Form1.Designer.cs to verify everything works as expected:

//
// userControl1
//
this.userControl1.BackColor = System.Drawing.Color.Maroon;
this.userControl1.Location = new System.Drawing.Point(50, 23);
this.userControl1.Name = "userControl11";
this.userControl1.Number = 1 + 3 + 23;
this.userControl1.Size = new System.Drawing.Size(686, 294);
this.userControl1.TabIndex = 0;

I’ll leave the implementation of the Deserialize method up to you. By adding the DesignerSerializer attribute to our IExtenderProvider implementations we can get full control over their code generation too :)

  1. Is it possible to serialize code inside event handlers, e.g.
    we have serialized
    this.Click += new System.EventHandler(this.Form1_Click);
    Form1.Designer.cs

    but how to serialize code like this:

    private void Form1_Click(object sender, EventArgs e)
    {
    // some code here serialized at design time
    }

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>