The ASP.NET MVC framework introduces the concept of returning an ActionResult in Controllers since the "preview preview" release on CodePlex. The purpose of this concept is to return a generic ActionResult object for each Controller method, allowing different child classes returning different results.
An example ActionResult (built-in) is the RenderViewResult. Whenever you want to render a view, you can simply return an object of this class which will render a specific view in its ExecuteResult method. Another example is the HttpRedirectResult which will output an HTTP header (Location: /SomethingElse.aspx).
In my opinion, this is a great concept, because it allows you to develop ASP.NET MVC applications more transparently. In this blog post, I will build a custom ActionResult class which will render an image to the HTTP response stream.
As an example, let's create a page which displays the current time as an image.
One option for implementing this would be creating an ASP.NET HttpHandler which renders this image and can be used inline with a simple HTML tag: <img src="DisplayTime.ashx" />
Wouldn't it be nice to be able to do something more ASP.NET MVC-like? Let's consider the following: <%=Html.Image<HomeController>(c => c.DisplayTime(), 200, 50, "Current time")%>
1. Creating the necessary HtmlHelper extension methods
The above example code is not available in standard ASP.NET MVC source code. We'll need an extension method for this, which will map a specific controller action, width, height and alternate text to a standard HTML image tag:
[code:c#]
public static class ImageResultHelper
{
public static string Image<T>(this HtmlHelper helper, Expression<Action<T>> action, int width, int height)
where T : Controller
{
return ImageResultHelper.Image<T>(helper, action, width, height, "");
}
public static string Image<T>(this HtmlHelper helper, Expression<Action<T>> action, int width, int height, string alt)
where T : Controller
{
string url = helper.BuildUrlFromExpression<T>(action);
return string.Format("<img src=\"{0}\" width=\"{1}\" height=\"{2}\" alt=\"{3}\" />", url, width, height, alt);
}
}
[/code]
2. The custom ActionResult class
Our new ImageResult class will inherit the abstract class ActionResult and implement its ExecuteResult method. This method basically performs communication over the HTTP response stream.
[code:c#]
public class ImageResult : ActionResult
{
public ImageResult() { }
public Image Image { get; set; }
public ImageFormat ImageFormat { get; set; }
public override void ExecuteResult(ControllerContext context)
{
// verify properties
if (Image == null)
{
throw new ArgumentNullException("Image");
}
if (ImageFormat == null)
{
throw new ArgumentNullException("ImageFormat");
}
// output
context.HttpContext.Response.Clear();
if (ImageFormat.Equals(ImageFormat.Bmp)) context.HttpContext.Response.ContentType = "image/bmp";
if (ImageFormat.Equals(ImageFormat.Gif)) context.HttpContext.Response.ContentType = "image/gif";
if (ImageFormat.Equals(ImageFormat.Icon)) context.HttpContext.Response.ContentType = "image/vnd.microsoft.icon";
if (ImageFormat.Equals(ImageFormat.Jpeg)) context.HttpContext.Response.ContentType = "image/jpeg";
if (ImageFormat.Equals(ImageFormat.Png)) context.HttpContext.Response.ContentType = "image/png";
if (ImageFormat.Equals(ImageFormat.Tiff)) context.HttpContext.Response.ContentType = "image/tiff";
if (ImageFormat.Equals(ImageFormat.Wmf)) context.HttpContext.Response.ContentType = "image/wmf";
Image.Save(context.HttpContext.Response.OutputStream, ImageFormat);
}
}
[/code]
3. A "DisplayTime" action on the HomeController
We'll add a DisplayTime action on the HomeController class, which will return an instance of the newly created class ImageResult:
[code:c#]
public ActionResult DisplayTime()
{
Bitmap bmp = new Bitmap(200, 50);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(Brushes.White, 0, 0, 200, 50);
g.DrawString(DateTime.Now.ToShortTimeString(), new Font("Arial", 32), Brushes.Red, new PointF(0, 0));
return new ImageResult { Image = bmp, ImageFormat = ImageFormat.Jpeg };
}
[/code]
And just to be complete, here's the markup of the index view on the HomeController:
[code:c#]
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<%@ Import Namespace="MvcApplication1.Code" %>
<%@ Import Namespace="MvcApplication1.Controllers" %>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<p>
<%=Html.Image<HomeController>(c => c.DisplayTime(), 200, 50, "Current time")%>
</p>
</asp:Content>
[/code]
Want the source code? Download it here! You can use it with the current ASP.NET MVC framework source code drop.