Thursday, November 10, 2011

Building A Color Picker Using ASP.NET MVC And jQuery


If you've used Microsoft's Ajax Control Toolkit, you'll probably have used a control called the ColorPicker. This is where it displays a list of colors that you can select from in a web page, and have the hex value of that color displayed when you click on it. I thought it would be a good exercise to show you how to do this using ASP.NET MVC and jQuery. Well here's how to do it. The example I’m demonstrating in this article is using the new jQuery 1.4 Alpha 2 release.  The end result will look like the screen cast below. If you cannot view the screencast shown below for some reason, you can directly view the screencast over here







To demonstrate this I’ve created an ASP.NET MVC application. For this example to work I obviously need some colors to display on the web page. Instead of hard coding a list of HTML hex values, the alternative approach is to retrieve the values from a source. The source for this example will be a list of known system colors in Windows. Let's start by creating a Colors controller that will return a HTML table with all the colors. Here's the code to do this:
C#
public class ColorController : Controller
{
      [AcceptVerbs(HttpVerbs.Get)]
      public JsonResult FetchColors()
      {
            var count = 0;
            var sb = new StringBuilder();
            sb.Append("<table><tbody><tr>");
           
            foreach (var color in Enum.GetNames(typeof(KnownColor)))
            {
                var colorValue = ColorTranslator.FromHtml(color);
                var html = string.Format("#{0:X2}{1:X2}{2:X2}",
                                    colorValue.R, colorValue.G, colorValue.B);
                sb.AppendFormat("<td bgcolor=\"{0}\">&nbsp;</td>", html);
                if (count < 20)
                {
                    count++;
                }
                else
                {
                    sb.Append("</tr><tr>");
                    count = 0;
                }
            }
            sb.Append("</tbody></table>");
            return Json(sb.ToString());
      }
}
VB.NET (Conversion Tool)
Public Class ColorController
      Inherits Controller
      <AcceptVerbs(HttpVerbs.Get)> _
      Public Function FetchColors() As JsonResult
             Dim count = 0
                  Dim sb = New StringBuilder()
                  sb.Append("<table><tbody><tr>")
 
                  For Each color In System.Enum.GetNames(GetType(KnownColor))
                        Dim colorValue = ColorTranslator.FromHtml(color)
                        Dim html = String.Format("#{0:X2}{1:X2}{2:X2}", colorValue.R, colorValue.G, colorValue.B)
                        sb.AppendFormat("<td bgcolor=""{0}"">&nbsp;</td>", html)
                        If count < 20 Then
                              count += 1
                        Else
                              sb.Append("</tr><tr>")
                              count = 0
                        End If
                  Next color
                  sb.Append("</tbody></table>")
                  Return Json(sb.ToString())
      End Function
End Class
I've decorated the action to only accept HTTP GET requests from the website by using the AcceptVerbs attribute. 
[AcceptVerbs(HttpVerbs.Get)]
It's important to explicitly state what request an action will accept. This way if a malicious user tries to post data to this action, the request will be ignored by the action. To get the collection of colors from the system I enumerate through the Enum.GetNames method by specifying the type as KnownColors. From there I retrieve the HTML hex value from the following two lines of code:
var colorValue = ColorTranslator.FromHtml(color);
var html = string.Format("#{0:X2}{1:X2}{2:X2}",
      colorValue.R, colorValue.G, colorValue.B);
 
I've also keeping track of how many <td> cells I create per row. Once the count reaches 20, I start a new <tr> row. If I didn't do this then the colors would print out entirely in the first <tr>. Doesn't look too appealing! Finally the generated HTML is returned as a JSON object.
 
All the server side code is finished now. The code below is for the view to render the color picker. I'll explain what I'm doing afterwards:
 
<script language="javascript" type="text/javascript" src="http://code.jquery.com/jquery-1.4a2.js"></script>
<script language="javascript" type="text/javascript">
        $(function() {
            $("#Colors").hide();
            $("#SelectColor").click(function() {
                $.get("/Color/FetchColors"nullfunction(data) {
                    $("#Colors").html(eval(data));
                });
                $("#Colors").toggle();
            });
 
            $("td").live("mouseover", (function() {
                $("#Sample").css("background-color", $(this).css("background-color"));
                $(this).css("cursor""pointer");
            }));
 
            $("td").live("click"function() {
                $("#SelectedColor").val($(this).attr("bgcolor"));
            }); 
        }); 
</script>   
<input type="text" id="SelectedColor" name="SelectedColor" readonly="readonly" />
<img src="/Content/Images/cp_button.png" alt="Pick a color" align="absmiddle" id="SelectColor" />
<span id="Sample">&nbsp;&nbsp;&nbsp;&nbsp;</span><br /><br />
<div id="Colors"></div>
 
Firstly when the user clicks on the color palette image, this will trigger the Ajax call to the server. This is done by using jQuery's $.get function. The first parameter is the URL. In ASP.NET MVC you pass in the controller/action. Once the JSON has been returned, I'm assigning it to the Colors <div> tag. 
$("#SelectColor").click(function() {
      $.get("/Color/FetchColors"nullfunction(data) {
            $("#Colors").html(eval(data));
      });
      $("#Colors").toggle();
});
Because the color selector is being generated dynamically, I can't use bind to bind an event handler to the click and mouseover events. For this to work I need to use jQuery's liveevent. The live event binds a handler to an event for all current - and future - matched elements:
$("td").live("mouseover", (function() {
      $("#Sample").css("background-color", $(this).css("background-color"));
      $(this).css("cursor""pointer");
}));
 
$("td").live("click"function() {
      $("#SelectedColor").val($(this).attr("bgcolor"));
});
Now when the user clicks on any color, the hex value will be displayed in the text box. This is a nice and easy way to add more richness to your application through jQuery. The entire source code of this article can be downloaded over here