ASP.NET Web Forms Extensibility: Control Builder Interceptors

After my previous post on Control Builders, what could possibly come next? Of course, Control Builder Interceptors! Not much documentation on this one, which is a shame, because it is an even more powerful feature that was recently introduced in ASP.NET 4.5.

A Control Builder Interceptor inherits from, unsurprisingly, ControlBuilderInterceptor. This is configured for the whole application, in the Web.config file, in the compilation section, by a controlBuilderInterceptorType (sorry, no link, since the ASP.NET 4.5 documentation is not online) attribute:

<compilation targetFramework="4.5" controlBuilderInterceptorType="MyNamespace.MyControlBuilderInterceptor, MyAssembly" />

Similarly to Control Builders, a Control Builder Interceptor allows us to:

Granted, less than Control Builders, but the point here is that this is fired for all markup-declared controls, not just those that have a specific Control Builder applied to. With that in mind, we can write code like this:

public class MyControlBuilderInterceptor : ControlBuilderInterceptor

{

    //raised for every control on markup

    public static event Action<ControlInterceptedEventArgs> ControlIntercepted;

 

    public override void OnProcessGeneratedCode(ControlBuilder controlBuilder, CodeCompileUnit codeCompileUnit, CodeTypeDeclaration baseType, CodeTypeDeclaration derivedType, CodeMemberMethod buildMethod, CodeMemberMethod dataBindingMethod, IDictionary additionalState)

    {

        var controlDeclaration = buildMethod.Statements[0] as CodeVariableDeclarationStatement;

 

        if (controlDeclaration != null)

        {

            var controlName = controlDeclaration.Name;

 

            buildMethod.Statements.Insert(buildMethod.Statements.Count - 1, new CodeSnippetStatement(String.Concat(this.GetType().FullName, ".Intercept(@", controlName, ");")));

        }

 

        base.OnProcessGeneratedCode(controlBuilder, codeCompileUnit, baseType, derivedType, buildMethod, dataBindingMethod, additionalState);

    }

 

    public override void PreControlBuilderInit(ControlBuilder controlBuilder, TemplateParser parser, ControlBuilder parentBuilder, Type type, String tagName, String id, IDictionary attributes, IDictionary additionalState)

    {

        if ((attributes != null) && (attributes.Contains("Text") == true))

        {

            //make property value uppercase

            attributes["Text"] = (attributes["Text"] as String).ToUpper();

        }

 

        base.PreControlBuilderInit(controlBuilder, parser, parentBuilder, type, tagName, id, attributes, additionalState);

    }

 

    public static void Intercept(Control instance)

    {

        var handler = ControlIntercepted;

 

        if (handler != null)

        {

            handler(new ControlInterceptedEventArgs(instance));

        }

    }

}

And there you have it. By adding an event handler to MyControlBuilderInterceptor.ControlIntercepted, we can analyze and change the properties of every control:

[Serializable]

public sealed class ControlInterceptedEventArgs : EventArgs

{

    public ControlInterceptedEventArgs(Control control)

    {

        this.Control = control;

    }

 

    public Control Control { get; private set; }

}

 

MyControlBuilderInterceptor.ControlIntercepted += e =>

{

    var myControl = e.Control as MyControl;

    

    if (myControl != null)

    {

        myControl.Text = myControl.Text.ToUpper();

    }

};

Stay tuned for more extensibility points of your favorite framework!

Published by

Ricardo Peres

Tech Lead at RedLight Software.

Leave a Reply

Your email address will not be published. Required fields are marked *