Updated Content Available
Check out my new series on using WebControls inside of ASP.NET MVC View Pages.
Pop Quiz – What happens with the following snippet of ASP.NET code?
<% int number = 5; %>
<asp:PlaceHolder runat="server" >
<% =number %>
</asp:PlaceHolder>
Prints the number 5? Nope. Maybe it equals zero? Sorta. How about this…
Compiler Error Message: CS0103: The name ‘number’ does not exist in the current context
Yikes…
I’ve complained before about the disconnect between WebControls and actual inline code. WebControls are still a very convenient way to write templates but because they exist in a different context than inline code they are effectively off limits. As cool as MVC is, you’re pretty much stuck throwing all your existing WebControls out the window. Or are you?
Using Extension Methods Instead of Controls
Extension Methods came in really at the best time possible. I can’t see MVC working without them.
If you’ve never used one before, an Extension Method lets you create a static
method elsewhere in your project, do a couple fancy assignments and then it attaches that method onto the class you’re targeting. LINQ heavily relies on Extension Methods to provide such a seamless programming experience.
One way that ASP.NET MVC uses Extension Methods is to make working with certain control types easier. For example, there is a method to create the input
tag, one to render a form
tag, etc.
Below is an example of how you could create an Extension Method that is attached to the HtmlHelper
.
public static class MyExtensionMethods {
public static string BulletList(this HtmlHelper helper, params string[] items) {
return string.Concat(
"<ul><li>",
string.Join("</li><li>", items),
"</li></ul>"
);
}
}
In our example, we create a static
class to house our Extension Methods. We also create static
methods with a strange argument at the start. This argument is actually the class were attaching the method to. Now we can use our code like so.
<% =this.Html.BulletList("Apple", "Orange", "Pear") %>
Cool. If you’re not familiar with the things you can do with Extension Methods, then I recommend you read about them some more before you start trying to add them to your project. You could also use delegates to simulate templating within an Extension Method.
public static class MyExtensionMethods {
public static void TwoColumns(this HtmlHelper helper, Action left, Action right) {
HttpContext.Current.Response.Write("<div class='left'>");
left();
HttpContext.Current.Response.Write("</div>");
HttpContext.Current.Response.Write("<div class='right'>");
right();
HttpContext.Current.Response.Write("</div>");
}
}
Then you can use your “template” like so…
<% this.Html.TwoColumns(() => { %>
I'm on the left!
<% }, () => { %>
I'm on the right!
<% }); %>
Code like this can get ugly in a hurry – so be conservative in your use.
Using IDisposable To Close Tags
Another way you can create a “WebControl
” with ASP.NET MVC is to create a class that implements IDisposable
. By placing markup in the constructor and the Dispose
method, you can essentially write your RenderBeginTag()
and RenderEndTag()
methods you normally find on CompositeControls
!
public class StyledHeader : IDisposable {
public StyledHeader(string color) {
HttpContext.Current.Response.Write("<h1 style='color:" + color + "' >");
}
public void Dispose() {
HttpContext.Current.Response.Write("</h1>");
}
}
Naturally, StyledHeader
should have been added to the core of the ASP.NET MVC library, but somehow it got missed . In any case, our class can be used with the using
keyword to render our fancy new header.
<% using (new StyledHeader("#f00")) { %>
Howdy - This is my header control!
<% } %>
The Super Secret Final Method
As you noticed at the beginning of my post, I mentioned about throwing away WebControls
since they aren’t any use to us anymore. Well, that isn’t true — We can still use WebControl
with our inline code for the page!
If you’ve read any of my previous blog posts, you can see that I’m a big fan of overriding the Render()
method for WebControls. In a similar fashion, we’re going to use the RenderControl()
method to render our WebControl
s right when we need them.
using System.Reflection;
using System.IO;
using System.Web.UI;
using System.Web;
using System.Web.Mvc;
public static class MyExtensionMethods {
public static void RenderControl(this HtmlHelper helper, Control control) {
MethodInfo bind = control.GetType().GetMethod("DataBind");
if (bind is System.Reflection.MethodInfo) {
bind.Invoke(control, null);
}
StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);
control.RenderControl(html);
HttpContext.Current.Response.Write(writer.ToString());
html.Dispose();
writer.Dispose();
}
}
You may notice the courtesy DataBind()
call we’re doing there — Just in case something has a DataSource
I was calling the method as well. Depending on how you use this, you may want to change this a little. But enough of that, how is it used?
<% int[] numbers = { 1, 2, 3, 4, 5 }; %>
<% this.Html.RenderControl(new DataGrid() { DataSource = numbers }); %>
You can also define your class before you pass it into the RenderControl
method in case you need to do a little more to it than just assign some values to the properties.
Finally, WebForms and MVC In Harmony… Or Maybe Not…
Now, I won’t pretend that you can plug all of your WebControl
s into this and expect it to work like WebForms used to. A lot of things are missing that a lot of WebControl
s rely on (like the ViewState
). But, if you are mainly interested in the rendered output of a WebControl
, then you’re in luck.