Binding bordered images to GridViews

Here’s a neat trick: bind an image with a border to a GridView!
The first rule of web development is “nothing is as easy as it seems.” I think the second rule is something about choosing from standards? I don’t really care. The point is, this seemed like it would be too easy, but the implementation threw some curves at me. Let’s hearken back to a simpler time and begin our journey with a list of possible horrors:

  • The image object links to a physical location. It does not write BLOB objects. Friggen nothing writes BLOB objects.
  • Sometimes there are null values.
  • I need a border. This will create a little empty black square if a null is encountered.
  • What the heck is the difference between <%# and <%= again? And how the heck do I search for it? (Look here for explanation and links. Please read this. You’ll be so happy you did.)

Now we follow our hero on her continued path of – yes, hero is the male form. Don’t judge me. You don’t know – discovery. The first step is, of course, to point the Image object to an HttpHandler and pass that the ID for the data row containing the image. The handler itself is a pretty straightforward interface. There’s only one method: ProcessRequest() where you need to implement a method to grab the data from SQL. There’s also one property:  good ol’ IsReusable(). IsReusable is a boolean value that indicates whether or not the HttpHandler object can be reused. There’s a lot to take into consideration here:

  • Threading
    • If you have multiple threads accessing this at the same time you can completely screw up the state. What I’m doing is getting the query string within the process request. This is fine since the variable is local to the method it will be unique to the thread. You don’t have to stick to my code, though. If you are reusing the object, remember that any thread can, at any time, be changing a global variable.
  • End state of the handler
    • This is especially important here, since we’re using a lot of data objects, we need to make sure everything is properly disposed before the method is done executing.
  • Memory considerations
    • If IsReusable is false, then a new handler will be created for each request. This can cause performance issues if you have a lot of, say, users, accessing this, say, all at once. However, a simple httphandler doesn’t usually cause a lot of memory bloat. This is one of those application-specific things.

Whether you set IsReusable to true or false really comes down to what you need this handler to do. If you’re really uncertain, leave it at false – it will at least guarantee there are no multithreading nightmares, like in the sopranos.

So, I set my IsReusable to false. I also pass the ID rather than the image name, so I can get the image from the handler with a simple query. I took a lot of tips and some code from Pankaj Mishra’s article, and what I ended up with for ProcessRequest was roughly this:

public void ProcessRequest(HttpContext context)
    {
     //Get the query string, if you recall, it's the row ID
        string id = context.Request.QueryString["id"];

        if (!string.IsNullOrEmpty(id))
        {
            //Make that string an int!
            int ID = Convert.ToInt32(id);

            MemoryStream memoryStream = new MemoryStream();
            SqlConnection connection = new SqlConnection(ConnString);
            string sql = "SELECT image FROM MyDatabase WHERE ID = @ID";

            SqlCommand cmd = new SqlCommand(sql, connection);
            cmd.Parameters.AddWithValue("@ID", ID);
            connection.Open();

            SqlDataReader reader = cmd.ExecuteReader();
            reader.Read();

            //Get Image Data
            if (reader["image"] != DBNull.Value)
            {
      //Read up the BLOB
                byte[] file = (byte[])reader["image"];
      //Close your objects
                reader.Close();
                connection.Close();
      //Write the file and send the response
                memoryStream.Write(file, 0, file.Length);
                context.Response.Buffer = true;
                context.Response.BinaryWrite(file);
            }
            //Now get rid of the last thing
            memoryStream.Dispose();
        }
    }

The final leg of our adventure finds the hero, weary and victorious, resolving the little black box for a null value. Base the visible property on the returned byte stream being a null value. You can cast it to a string. It really doesn’t matter if you bind the image here, since we’re just looking for a null value.

Like this (I love ternary statements because a part of me still thinks FORTRAN’s 1995 standard changes are AWESOME):

Visible='<%# (Eval("image").ToString() == "")?false:true %>'

Now you set the image URL to the image handler address and concat the ID as so:

<%# “/Namespace/Page/ImgHandler.ashx?id=" + Eval("ID") %>

Now the ID gets sent to the handler on binding. Then the image URL is the handler address, which returns the picture!

And here’s the image tag altogether:

<asp:Image ID="ImageBind" runat="server" BorderStyle="Solid" BorderColor="Black"
    Visible='<%# (Eval("image").ToString() == "")?false:true %>' BorderWidth="1px"
            Width="100px" ImageUrl='<%# “/Namespace/Page/ImgHandler.ashx?id=" + Eval("ID") %>' />

And the hero and the really awesome hawk that I just now decided she has, have bound images to a GridView. You’re all welcome.

Related posts: