If you’ve ever written any non-trivial ASP.NET control, you’re probably familiar with the concept of a Control Builder. Basically, it’s a class that you associate with your control and that affects the way your control gets processed at parse time. While ControlBuilder has been around since the ASP.NET 1.0 days, a very powerful new feature was added to it in 3.5 (i.e. VS 2008). Unfortunately, we never had a chance to tell people about it, and a web search reveals that essentially no one knows about it! Pretty unfortunate, and obviously, the point of this post is to change that. :-)
So what is this super cool feature? Simply put, it lets the ControlBuilder party on the CodeDom tree used for code generation of the page. That means a ControlBuilder can inspect what’s being generated, and make arbitrary changes to it.
Warning: this post assumes some basic knowledge of CodeDom. If you are not familiar with it, you may want to get a basic introduction to it on MSDN or elsewhere before continuing.
To use this feature, all you have to do is override the new ProcessGeneratedCode() method on ControlBuilder. here is what this method looks like:
// // Summary: // Enables custom control builders to access the generated Code Document Object // Model (CodeDom) and insert and modify code during the process of parsing // and building controls. // // Parameters: // codeCompileUnit: // The root container of a CodeDOM graph of the control that is being built. // // baseType: // The base type of the page or user control that contains the control that // is being built. // // derivedType: // The derived type of the page or user control that contains the control that // is being built. // // buildMethod: // The code that is used to build the control. // // dataBindingMethod: // The code that is used to build the data-binding method of the control. public virtual void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit, CodeTypeDeclaration baseType, CodeTypeDeclaration derivedType, CodeMemberMethod buildMethod, CodeMemberMethod dataBindingMethod);
So basically you get passed a bunch of CodeDom objects and you get to party on them. It may seem a bit confusing at first to get passed so many different things, but they all make sense in various scenarios.
Tip to make more sense of all that stuff: a great way to learn more about the code ASP.NET generate is simply to look at it! To do this, add debug=”true” on your page, add a compilation error in there (e.g. <% BAD %>) and request the page. In the browser error page, you’ll be able to look at all the generated code.
Let’s take a look at a trivial sample that uses this. It doesn't do anything super useful but does demonstrate the feature. First, let’s write a little control that uses a ControlBuilder:
[ControlBuilder(typeof(MyGeneratingControlBuilder))] public class MyGeneratingControl : Control { // Control doesn't do anything other than generate code via its ControlBuilder }
Now in the ControlBuilder, let’s implement ProcessGeneratedCode so that it spits out a little test property:
// Spit out a property that looks like: //protected virtual string CtrlID_SomeCoolProp { // get { // return "Hello!"; // } //} var prop = new CodeMemberProperty() { Attributes = MemberAttributes.Family, Name = ID + "_SomeCoolProp", Type = new CodeTypeReference(typeof(string)) }; prop.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression("Hello!"))); baseType.Members.Add(prop);
So it just generates a string property with a name derived from the control ID. Now let’s look at the page:
<test:MyGeneratingControl runat="server" ID="Foo" />
And finally, let’s use the generated property in code. The simple presence of the this tag allows me to write:
Label1.Text = Foo_SomeCoolProp;
And the really cool things is that Visual Studio picks this up, giving you full intellisense on the code generated by your ControlBuilder. How cool is that! :)
Full runnable sample is attached to this post.
At first glance, it may seem like this feature gives too much power to ControlBuilders, letting them inject arbitrary code into the page that’s about to run. The reality is that it really doesn’t let an evil control do anything that it could have done before. Consider those two cases:
ProcessGeneratedCode is a pretty powerful feature, giving your ControlBuilders full control over the code generation. It’s also a pretty advanced feature, and you can certainly shoot yourself in the foot with it if you’re not careful. So be careful!