Selecting a date comes up frequently in web applications, so in this post I’m going to be walking us through building an extension method that will generate a jQuery datepicker input that will seamlessly bind with model data on an action post-back. So let’s get started!

* Download the complete project here:

Downloading DatePicker Component & Setting Up References

The jQuery DatePicker script is not part of the core download.  It’s a separate download from the jQuery UI, so you’ll need to download that first.  The download page is structured as a package download for the various components, but you can successfully get everything you need by un-checking all but the datepicker component and clicking the download button:

image

Download the file and extract it into a (clean) directory. 

While the download does contain a custom javascript file to include the components you downloaded, we’re going to skip that script file and extract out only the datepicker components we need.

From the directory you extracted the download file, navigate to ‘development-bundle\themes\base’, and copy the contents of that directory into the ‘Content’ directory of your solution ( do this through visual studio and it’ll automatically include the files into the project):

image

Now navigate over to ‘development-bundle\ui’ in the directory you extracted the files to and copy ‘ui.core.js’ & ‘ui.datepicker.js’ into the ‘Scripts’ folder in your solution:

image

No Calendar is complete without an icon, so go ahead and grab this one:

http://www.codesprouts.com/Images/calendar.gif

And copy it into your ‘Content/images’ folder in the project:

image

That’s it for resources, but we need to setup references in order to use jQuery & access the css files.  I’m using a brand-new project and will be working in the ‘Views/Home/Index.aspx’ view, so I can add script & css references ot the ‘Views/Shared/Site.Master’ file and they will be available in the Index.aspx page.  I added the following references into the <head> element of the master:

<link href="../../Content/ui.all.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="../../Scripts/jquery-1.3.2.js"></script>
<script type="text/javascript" src="../../Scripts/ui.core.js"></script>
<script type="text/javascript" src="../../Scripts/ui.datepicker.js"></script>

And with that, we should be able to start playing with our DatePicker extension!

Working With The DatePicker

Before we can wrap everything up into an extension method, we need to get the base functionality working first.  If you’re using a brand new project, navigate to your Index page, and include the following code in the view:

Date: <input type="text" id="date" />
    
<script type="text/javascript">
     $(function() {
          ("#date").datepicker();
     });
</script>

Run the project, and you should now see a functioning date picker when you click in the textbox:

image 

Excellent!

In our date input above, we’re using the defaults for all options of the datepicker, so let’s change that a bit before we wrap this up into a helper extension.

I’d rather have the datepicker show immediately, and have an image button for the popup, so we’ll need to change the call to .datepicker() to the following:

$("#date").datepicker({
                showOn: 'button',
                buttonImage: '/Content/images/calendar.gif',
                duration: 0 
                });

You can find the description for all options at http://docs.jquery.com/UI/Datepicker .

If you run the site to view the lastest changes, you’ll now see a pretty calendar icon that can be clicked to popup the calendar:

image

Perfect!

Creating A Helper Extension

Now that we’ve created a working date picker, let’s put it into an extension method in order to clean up the UI & allow us to use it elsewhere.

Create a new folder in the root of the project, called ‘UI’, and create a new class called ‘HtmlHelperExtensions.cs’ in our new folder:

image

In the new class, add the following method to the class ( You’ll need to add a using clause to System.Web.MVC & System.Text ):

public static string DatePicker(this HtmlHelper helper, string name, string imageUrl, object date)
{
    StringBuilder html = new StringBuilder();
 
    // Build our base input element
    html.Append("<input type=\"text\" id=\"" + name + "\" name=\"" + name + "\"");
 
    // Model Binding Support
    if (date != null)
    {
        string dateValue = String.Empty;
 
        if (date is DateTime? && ((DateTime)date) != DateTime.MinValue)
            dateValue = ((DateTime)date).ToShortDateString();
        else if (date is DateTime && (DateTime)date != DateTime.MinValue)
            dateValue = ((DateTime)date).ToShortDateString();
        else if (date is string)
            dateValue = (string)date;
 
        html.Append(" value=\"" + dateValue + "\"");
    }
 
    // We're hard-coding the width here, a better option would be to pass in html attributes and reflect through them
    // here ( default to 75px width if no style attributes )
    html.Append(" style=\"width: 75px;\" />");
 
    // Now we call the datepicker function, passing in our options.  Again, a future enhancement would be to
    // pass in date options as a list of attributes ( min dates, day/month/year formats, etc. )
    html.Append("<script type=\"text/javascript\">$(document).ready(function() { $('#" + name + "').datepicker({ showOn: 'button', buttonImage: '" + imageUrl + "', duration: 0 }); });</script>");
 
    return html.ToString();
}

Since we’ve added it as an extension method, if we add the namespace into the view web.config, we’ll be able to call our new method from the HtmlHelper of the view. 

To add the namespace, open up your web.config ( you can either use the web.config in the ‘View’ folder, or go to the root and add it there ), and add the following into the namespaces element:

<add namespace="YourProjectNameGoesHere.UI"/>

In my example project, the name of the project is ‘DatePickerHarness’, so my complete namespaces element looks like the following:

<namespaces>
    <add namespace="System.Web.Mvc"/>
    <add namespace="System.Web.Mvc.Ajax"/>
    <add namespace="System.Web.Mvc.Html"/>
    <add namespace="System.Web.Routing"/>
    <add namespace="System.Linq"/>
    <add namespace="System.Collections.Generic"/>
    <add namespace="DatePickerHarness.UI"/>
</namespaces>

Now that we’ve got that in there, we can access the extension method from within our view:

image

Now that we have the abillity to call our extension method, let’s make this a bit more exciting and wrap everything into a form that will post back to an action in our controller.  First we’ll need to add our complete form to the frontend:

<% using (Html.BeginForm("AddAWeek", "Home"))
   { %>
<table>
    <tr>
        <td>
            Date:
        </td>
        <td>
            <%= Html.DatePicker("Date", "/Content/images/calendar.gif", 
                                this.ViewData["TheDate"]) %>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <input type="submit" value="Add A Week" />
        </td>
    </tr>
</table>
<% } %>

And a bit of code to our controller:

public ActionResult AddAWeek(DateTime? Date)
{
    if (Date == null)
        Date = DateTime.Now;
 
    Date = Date.Value.AddDays(7);
 
    this.ViewData["TheDate"] = Date;
    
    return View("Index");
}

And then we can run it:

image

Changing the date to May 18th, and hitting ‘Add A Week’ displays the following after the post-back:

image

Fantastic!

The Wrap-Up

With just a little bit of work, we’ve now created a reusable component for displaying & capturing dates with the ASP.Net MVC Framework. Any view we create within the project now has the new DatePicker extension method available, significantly cutting down on the time it would take to code it from scratch each time.

The include project contains everything we’ve done here, and also includes a few overloads to make the DatePicker extension a bit more robust.  If you’re not happy with the way the datepicker works or how it looks, feel free to change the rendered script in the extension method(s) as you see fit.

Till next time!

- Colin