Tuesday, September 8, 2009

Anatomy of an ASP.NET MVC Control

One of the great things about ASP.NET WebForms was that it wasy very easy to creat reuseable custom controls and components, but with appearance of ASP.NET MVC and lots of developers moving towards it – which is a good thing – what happens to component oriented development? Is is possible to create custom controls in MVC? Can we port our existing controls to MVC? ASP.NET MVC while may not be as good as WebForms when it comes to drag-and-drop style of using components, but it is fairly easy to use and to create custom controls in MVC! Let’s see how.

To create a control in WebForms world, you’d extend the existing “WebControl” class and override Render method which takes a HtmlTextWriter that you can use to output the desired HTML code to the browser. A control rendering “Hello World” on the screen would be as simple as this:

public class HelloWorldControl : WebControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        base.Render(writer);

        writer.Write("<span>HelloWorld</span>");
    }
}
You’d put this on your web page and you’re all set. But what’s the best approach in MVC controls? There’s no WebControl or Control classes inside Mvc assemblies, so can either use any existing class you have as a base class or it from scratch.
public class WebControl
{
    /// <summary>
    /// Renders the control by writing the html code into the text writer.
    /// </summary>
    /// <param name="textWriter"></param>
    public virtual void Render(TextWriter textWriter)
    {
    }

    /// <summary>
    /// Renders the control and outputs the generated HTML code.
    /// </summary>
    /// <returns></returns>
    public string OnRender()
    {
        var tw = new StringWriter();

        Render(tw);

        return tw.ToString();
    }
}
With this, you have a ready to use base class for your controls. You need to override the Render method and construct the right HTML code. The code would look almost identical:
public class HelloWorldControl : WebControl
{
    public override void Render(TextWriter writer)
    {
        base.Render(writer);

        writer.Write("<span>Hello World!</span>");
    }
}
Note: There’s a catch here. We’d use existing methods on HtmlTextWriter in WebForms, which emitted correct HTML code per Browser. If you’re writing the HTML code manually (as I did in this example) you’d be careful in case you need to support different browsers.

The only thing remaining is, how do we use this in an MVC application? The way to do this, as it is already done in the MVC framework with other controls such as TextBox, CheckBox, etc. is to create an extension method on HtmlHelper class. HtmlHelper is just there as a waypoint for extension method and has no other uses:
public static class HelloWorldControlExtensions
{
    public static string HelloWorld(this HtmlHelper helper)
    {
        var control = new HelloWorldControl();

        return control.OnRender();
    }
}
Now you can add this controls to your views, as easy as other controls such as TextBoxes:
<%= Html.HelloWorld() %>

That’s almost it. The only thing remaining here is to implement control specific logic of your components and that part is left to you.
Submit this story to DotNetKicks Shout it

No comments: