C# ASP.NET: Generating Image Buttons with Dynamic text

After writing several websites with custom buttons. I’ve had it with having to create images for each button that has a different text!

If you feel the same way then you’ll love this solution. With the code below you’ll be able to create a button.aspx page that takes a “t” url query parameter where you can specify the text and it will return the desired image. Here are some usage examples:

http://siteurl.com/button.aspx?t=Yes!” will give you:

http://siteurl.com/button.aspx?t=BACK&m=t” will give you:

http://siteurl.com/button.aspx?t=CONTINUE” will give you:

Even though the links to the image are not your usual *.png or *.jpg, the button.aspx page returns an image (content type image/x-png or image/jpeg) so it is OK to do the following in your aspx code:

<asp:ImageButton runat="server" ID="btnFormBack" ImageUrl="button.aspx?t=Continue" />

OR

<a href="targeturl.html"><img src="button.aspx?t=Continue" /></a>

Pretty cool ha!

Well, and now to the code. This is your button.aspx file:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="button.aspx.cs" Inherits="controls_button" %>
<%@ OutputCache VaryByParam="m;t" Duration="9123123" %>

And your button.aspx.cs file:

public partial class controls_button : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var text = this.Request.QueryString["t"];
        if (string.IsNullOrEmpty(text))
            throw new ApplicationException("The text url parameter must be specified");

        var sMirror = this.Request.QueryString["m"];
        bool mirror = (!string.IsNullOrEmpty(sMirror) && sMirror == "t");

        var rightImageWidth = 10; // in pixels
        var imageHeight = 42;
        var topPadding = 11; // top and bottom padding in pixels
        var sidePadding = 10; // side padding in pixels
        var textBrush = new SolidBrush(Color.White);
        var font = new Font("Arial Black", 11);


        //-------- Calculate the text's width
        // this graphics and bitmap object is just temporary. I could not 
        // find a way to calculate the text' width witout having one.
        var bitmap = new Bitmap(300, 300);
        var graphics = Graphics.FromImage(bitmap);
        var textSize = graphics.MeasureString(text, font);
        bitmap.Dispose();
        graphics.Dispose();

        //-------- Create the graphics object
        var bitmapWidth = sidePadding * 2 + (int)textSize.Width;
        bitmap = new Bitmap(bitmapWidth, imageHeight);
        //bitmap = new Bitmap(500, 40);
        graphics = Graphics.FromImage(bitmap);

        // Draw the background
        var leftImage = System.Drawing.Image.FromFile(this.Server.MapPath("~/resources/button-left.png"));
        var rightImage = System.Drawing.Image.FromFile(this.Server.MapPath("~/resources/button-right.png"));
        graphics.DrawImage(leftImage, 0, 0, bitmapWidth - rightImageWidth, imageHeight);
        graphics.DrawImage(rightImage, bitmapWidth - rightImageWidth, 0, rightImageWidth, imageHeight);
        // These disposes are necessary, otherwise the files get locked 
        leftImage.Dispose();
        rightImage.Dispose();

        if (mirror)
            bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX);

        // Draw the text
        graphics.DrawString(text, font, textBrush, sidePadding, topPadding);

        
        //-------- Serve the Image

        this.Response.ContentType = "image/x-png";
        
        // The line below breaks on the production and development servers
        // (it does not break on my computer). I was getting the following
        // vague exception "A generic error occurred in GDI+". I found this 
        // post that showed
        // me the work around http://aspalliance.com/319 . In short it says
        // that some image formats (like Png) require a seekable stream when 
        // saving. The Response.OutputStream is not seekable so we
        // have to use a MemoryStream as an intermediary.
        //bitmap.Save(this.Response.OutputStream, ImageFormat.Png);

        var memStream = new System.IO.MemoryStream();
        bitmap.Save(memStream, ImageFormat.Png);
        memStream.WriteTo(this.Response.OutputStream);

        // Some cleanup, not sure if it is all needed
        this.Response.End();
        memStream.Dispose();
        graphics.Dispose();
        bitmap.Dispose();
    }
}

And you also need to have these images in the ~/resources folder of your website. If you want to have them in another folder, update the .cs code.

~/resources/button-left.png :

~/resources/button-right.png :

Enjoy!