Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ASP.NET MVC3: Create a custom calendar/datepicker

0.00/5 (No votes)
6 Apr 2012 1  
Create your own custom calendar/datepicker for your project

Download Calendar.zip 

Introduction

In this article, we will show you how to create a custom calendar in ASP.NET MVC3. The steps are simple which follows MVC paradigm to create model till navigation between months. 

Background

There are few datepicker plugin exist in the community which developers can use in their application, a popular example is Jquery datepicker plugin. However, we might come to a scenario that we need to customize a datepicker (or we call it as calendar) to incorporate our data to be shown or even styling. Of course, you can modify codes from any plugin to fit your scenario; however, it is easier to build your own custom calendar that can differs your application from others. One of an example of scenario is where a HIS application needs to show daily patient and treatment on each date, the first thing that comes to our developer’s mind is that we need to build our own datepicker.

If calendar is customized to show data, for example insert simple notification of each day.

Using the code

  • Create a model - CalendarModel.
  • Create a service class for the logic and translation for the date.
  • Create a controller and actions that call the service.
  • Create a view which can show current month.
  • Create a JavaScript (jQuery library) to navigate between months

Let's start by create a new model called CalendarModel that define the classes needed for this project.

public class WeekForMonth
{
    public List<Day> Week1 { get; set; } //days for week1
    public List<Day> Week2 { get; set; } //days for week2
    public List<Day> Week3 { get; set; } //days for week3
    public List<Day> Week4 { get; set; } //days for week4
    public List<Day> Week5 { get; set; } //days for week5
    public List<Day> Week6 { get; set; } //days for week6
    public string nextMonth { get; set; }
    public string prevMonth { get; set; }
}

public class Day
{
    public DateTime Date { get; set; }
    public string _Date { get; set; }
    public string dateStr { get; set; }
    public int dtDay { get; set; }
    public int? daycolumn { get; set; }
   
}

Create a service folder and class (we call it as CalendarManager.cs) to do the backend logic and translation for the date. The service class contains few functions that meant to calculate the day, date, and date column in the calendar. First, we need to call getDates(year,month) function to get each date for the month and year selected, it follows by knowing which week this date falls in for that month by GetWeekOfMonth(DateTime date). Complete our service by returning a model of WeekForMonth using getCalendar function which maps day and week number for every date in a month.

//main function to arrange each details of day into a month, function returns a list of weeks for a month
public WeekForMonth getCalender(int month, int year)
{

    WeekForMonth weeks = new WeekForMonth();
    weeks.Week1 = new List<Day>();
    weeks.Week2 = new List<Day>();
    weeks.Week3 = new List<Day>();
    weeks.Week4 = new List<Day>();
    weeks.Week5 = new List<Day>();
    weeks.Week6 = new List<Day>();

    List<DateTime> dt = new List<DateTime>();
    dt = GetDates(year, month);

    foreach (DateTime day in dt)
    {
        switch (GetWeekOfMonth(day))
        {
            case 1:
                Day dy1 = new Day();
                        
                dy1.Date = day;
                dy1._Date = day.ToShortDateString();
                dy1.dateStr = day.ToString("MM/dd/yyyy");
                dy1.dtDay = day.Day;
                dy1.daycolumn = GetDateInfo(dy1.Date);
                weeks.Week1.Add(dy1);
                break;
            case 2:
                Day dy2 = new Day();
                dy2.Date = day;
                dy2._Date = day.ToShortDateString();
                dy2.dateStr = day.ToString("MM/dd/yyyy");
                dy2.dtDay = day.Day;
                dy2.daycolumn = GetDateInfo(dy2.Date);
                weeks.Week2.Add(dy2);
                break;
            case 3:
                Day dy3 = new Day();
                dy3.Date = day;
                dy3._Date = day.ToShortDateString();
                dy3.dateStr = day.ToString("MM/dd/yyyy");
                dy3.dtDay = day.Day;
                dy3.daycolumn = GetDateInfo(dy3.Date);
                weeks.Week3.Add(dy3);
                break;
            case 4:
                Day dy4 = new Day();
                dy4.Date = day;
                dy4._Date = day.ToShortDateString();
                dy4.dateStr = day.ToString("MM/dd/yyyy");
                dy4.dtDay = day.Day;
                dy4.daycolumn = GetDateInfo(dy4.Date);
                weeks.Week4.Add(dy4);
                break;
            case 5:
                Day dy5 = new Day();
                dy5.Date = day;
                dy5._Date = day.ToShortDateString();
                dy5.dateStr = day.ToString("MM/dd/yyyy");
                dy5.dtDay = day.Day;
                dy5.daycolumn = GetDateInfo(dy5.Date);
                weeks.Week5.Add(dy5);
                break;
            case 6:
                Day dy6 = new Day();
                dy6.Date = day;
                dy6._Date = day.ToShortDateString();
                dy6.dateStr = day.ToString("MM/dd/yyyy");
                dy6.dtDay = day.Day;
                dy6.daycolumn = GetDateInfo(dy6.Date);
                weeks.Week6.Add(dy6);
                break;
        };
    }

    while (weeks.Week1.Count < 7) // not starting from sunday
    {
        Day dy = null;
        weeks.Week1.Insert(0, dy);
    }

    if (month == 12)
    {
        weeks.nextMonth = (01).ToString() + "/" + (year + 1).ToString();
        weeks.prevMonth = (month - 1).ToString() + "/" + (year).ToString();
    }
    else if (month == 1)
    {
        weeks.nextMonth = (month + 1).ToString() + "/" + (year).ToString();
        weeks.prevMonth = (12).ToString() + "/" + (year - 1).ToString();
    }
    else
    {
        weeks.nextMonth = (month + 1).ToString() + "/" + (year).ToString();
        weeks.prevMonth = (month - 1).ToString() + "/" + (year).ToString();
    }

    return weeks;
}

//get all dates for a month for the year specified
public static List<DateTime> GetDates(int year, int month)
{
    return Enumerable.Range(1, DateTime.DaysInMonth(year, month))  // Days: 1, 2 ... 31 etc.
    .Select(day => new DateTime(year, month, day)) // Map each day to a date
    .ToList();
}

//get number of week for the selected month by passing in a date value
public static int GetWeekOfMonth(DateTime date)
{
    DateTime beginningOfMonth = new DateTime(date.Year, date.Month, 1);
    while (date.Date.AddDays(1).DayOfWeek != DayOfWeek.Sunday)
        date = date.AddDays(1);
    return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) +           1;
}

//translate each day to a day number for mapping to week
public int GetDateInfo(DateTime now)
{
    int dayNumber = 0;
    DateTime dt = now.Date;
    string dayStr = Convert.ToString(dt.DayOfWeek);

    if (dayStr.ToLower() == "sunday")
    {
        dayNumber = 0;
    }
    else if (dayStr.ToLower() == "monday")
    {
        dayNumber = 1;
    }
    else if (dayStr.ToLower() == "tuesday")
    {
        dayNumber = 2;
    }
    else if (dayStr.ToLower() == "wednesday")
    {
        dayNumber = 3;
    }
    else if (dayStr.ToLower() == "thursday")
    {
        dayNumber = 4;
    }
    else if (dayStr.ToLower() == "friday")
    {
        dayNumber = 5;
    }
    else if (dayStr.ToLower() == "saturday")
    {
        dayNumber = 6;
    }
    return dayNumber;
}

Create a controller in our Home Controller and calls the service. We need an action result for initial page and an asynchronous controller for ajax call to return our calendar model for next/previous month.

public ActionResult Index()
{
    var model = _calendar.getCalender(DateTime.Now.Month, DateTime.Now.Year);
    return View(model);
}

//for ajax request
public ActionResult AsyncUpdateCalender(int month, int year)
{
    if (HttpContext.Request.IsAjaxRequest())
    {
        var model = _calendar.getCalender(month, year);
        return Json(model, JsonRequestBehavior.AllowGet);
    }
    else
    {
        return View();
    }
}

Create a view page that generates the calendar html table form. The view page requires strongly typed model WeekForMonth. The view page has to add C# codes to loop the item in the model to our table with id="component-table" for display. Note: Please refer back to the image shown for our calendar display to have a clearer view on the result of html. We have header (for month name and navigation) and content (for days display).

@model Calendar.Models.WeekForMonth
<div id="component-header">
<a id="@Model.prevMonth" 
  class="month" style="float:left;margin-left:10px;">Prev</a> 
    @DateTime.Now.ToString("MMM") @DateTime.Now.Year <a id="@Model.nextMonth" 
    class="month" style="float:right;margin-right:10px;">Next</a>
</div>

<div>
        <table id="component-table">
            <thead>
            <tr>
                <th>
                    Sun
                </th>
                <th>
                    Mon
                </th>
                <th>
                    Tue
                </th>
                <th>
                    Wed
                </th>
                <th>
                    Thurs
                </th>
                <th>
                    Fri
                </th>
                <th>
                    Sat
                </th>
            </tr>
            </thead>
            <tbody>
                <tr id="week1">
                @foreach (var item in Model.Week1)
                {
                    if (item != null)
                    {
                        if (item.daycolumn == 0 || item.daycolumn == 6)
                        {
                            <td class="weekend"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else if (item.Date != DateTime.Today)
                        {
                            <td><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else
                        {
                            <td class="selected"><a id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                    }
                    else
                    {
                        <td></td>
                    }
                }
                </tr>
                <tr id="week2">
                @foreach (var item in Model.Week2)
                {
                    if (item != null)
                    {
                        if (item.daycolumn == 0 || item.daycolumn == 6)
                        {
                            <td class="weekend"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else if (item.Date != DateTime.Today)
                        {
                            <td><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else
                        {
                            <td class="selected"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                    }
                    else
                    {
                        <td></td>
                    }
                }
                </tr>
                <tr id="week3">
                @foreach (var item in Model.Week3)
                {
                    if (item != null)
                    {
                        if (item.daycolumn == 0 || item.daycolumn == 6)
                        {
                            <td class="weekend"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else if (item.Date != DateTime.Today)
                        {
                            <td><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else
                        {
                            <td class="selected"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                    }
                    else
                    {
                        <td></td>
                    }
                }
                </tr>
                <tr id="week4">
                @foreach (var item in Model.Week4)
                {
                    if (item != null)
                    {
                        if (item.daycolumn == 0 || item.daycolumn == 6)
                        {
                            <td class="weekend"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else if (item.Date != DateTime.Today)
                        {
                            <td><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else
                        {
                            <td class="selected"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                    }
                    else
                    {
                        <td></td>
                    }
                }
                </tr>
                 <tr id="week5">
                @foreach (var item in Model.Week5)
                {
                    if (item != null)
                    {
                        if (item.daycolumn == 0 || item.daycolumn == 6)
                        {
                            <td class="weekend"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else if (item.Date != DateTime.Today)
                        {
                            <td><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                        else
                        {
                            <td class="selected"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                        }
                    }
                    else
                    {
                        <td></td>
                    }
                }                    
                </tr>
                <tr id="week6">
                @if (Model.Week6 != null)
                {
                    foreach (var item in Model.Week6)
                    {
                        if (item != null)
                        {
                            if (item.daycolumn == 0 || item.daycolumn == 6)
                            {
                            <td class="weekend"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                            }
                            else if (item.Date != DateTime.Today)
                            {
                            <td><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3></a></td>
                            }
                            else
                            {
                            <td class="selected"><a  id="@item.dateStr" 
                              class="dt"><h3>@item.dtDay</h3> </a></td>
                            }
                        }
                        else
                        {
                        <td></td>
                        }
                    }
                }          
                </tr>
            </tbody>
            </table>
</div>

The JavaScript (jquery library) uses ajax call to do the instant update when click previous or next month. Sample of the script can be found in the project file too. The script works by asynchronously call an action result to get data for next/previous month, empty data from the initial table and lastly assign new data into the table.

Click on the next/previous link will trigger an event to AsyncUpdateCalendar by ajax call. We empty every week of the existing table, loop through the result received and append the result s by week number.

$(".month").live('click', function () {
    var object = $(this).attr("id");
    var str = object.split('/');
    // str[0] contains "month"
    // str[1] contains "year"
    $.ajax
    ({
        url: '../../Home/AsyncUpdateCalender',
        type: 'GET',
        traditional: true,
        contentType: 'application/json',
        data: { month: str[0], year: str[1] },
        success: function (result) {
            if (!jQuery.isEmptyObject(result)) {
                var week1 = $("#week1");
                week1.empty();
                var week2 = $("#week2");
                week2.empty();
                var week3 = $("#week3");
                week3.empty();
                var week4 = $("#week4");
                week4.empty();
                var week5 = $("#week5");
                week5.empty();
                var week6 = $("#week6");
                week6.empty();
                var newHeader = $('<a id=' + result.prevMonth + ' class="month" ' + 
                  'style="float:left">Prev</a>' + getMonth(str[0]) + ' ' + 
                  str[1] + '<a id=' + result.nex	            tMonth + 
                  ' class="month" style="float:right">Next</a>');
                $("#component-header").empty();
                $("#component-header").append(newHeader);
                $.each(result.Week1, function (i, item) {
                    var htmlStr = null;
                    if (jQuery.isEmptyObject(item)) {
                        htmlStr = $('<td></td>');
                        week1.append(htmlStr);
                    } else {
                        if (item.daycolumn == 0 || item.daycolumn == 6) {
                            htmlStr = $('<td class="weekend"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3></a>');
                        }
                                                
                        else if (item._Date != getTodayDate()) {                                
                            htmlStr = $('<td></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3></a>');
                        } else {
                            htmlStr = $('<td class="selected"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3></a>');
                        }
                        week1.append(htmlStr);
                    }
                });
                $.each(result.Week2, function (i, item) {
                    var htmlStr = null;
                    if (jQuery.isEmptyObject(item)) {
                        htmlStr = $('<td></td>');
                        week2.append(htmlStr);
                    } else {
                        if (item.daycolumn == 0 || item.daycolumn == 6) {
                            htmlStr = $('<td class="weekend"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        else if (item.Date != getTodayDate()) {
                            htmlStr = $('<td></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        } else {
                            htmlStr = $('<td class="selected"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        week2.append(htmlStr);
                    }
                });
                $.each(result.Week3, function (i, item) {
                    var htmlStr = null;
                    if (jQuery.isEmptyObject(item)) {
                        htmlStr = $('<td></td>');
                        week3.append(htmlStr);
                    } else {
                        if (item.daycolumn == 0 || item.daycolumn == 6) {
                            htmlStr = $('<td class="weekend"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        else if (item.Date != getTodayDate()) {
                            htmlStr = $('<td></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        } else {
                            htmlStr = $('<td class="selected"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + 
                              item.dtDay + '</h3> </a>');
                        }
                        week3.append(htmlStr);
                    }
                });
                $.each(result.Week4, function (i, item) {
                    var htmlStr = null;
                    if (jQuery.isEmptyObject(item)) {
                        htmlStr = $('<td></td>');
                        week4.append(htmlStr);
                    } else {
                        if (item.daycolumn == 0 || item.daycolumn == 6) {
                            htmlStr = $('<td class="weekend"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        else if (item.Date != getTodayDate()) {
                            htmlStr = $('<td></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        } else {
                            htmlStr = $('<td class="selected"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        week4.append(htmlStr);
                    }
                });
                $.each(result.Week5, function (i, item) {
                    var htmlStr = null;
                    if (jQuery.isEmptyObject(item)) {
                        htmlStr = $('<td></td>');
                        week5.append(htmlStr);
                    } else {
                        if (item.daycolumn == 0 || item.daycolumn == 6) {
                            htmlStr = $('<td class="weekend"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        else if (item.Date != getTodayDate()) {
                            htmlStr = $('<td></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        } else {
                            htmlStr = $('<td class="selected"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        week5.append(htmlStr);
                    }
                });
                $.each(result.Week6, function (i, item) {
                    var htmlStr = null;
                    if (jQuery.isEmptyObject(item)) {
                        htmlStr = $('<td></td>');
                        week6.append(htmlStr);
                    } else {
                        if (item.daycolumn == 0 || item.daycolumn == 6) {
                            htmlStr = $('<td class="weekend"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        else if (item.Date != getTodayDate()) {
                            htmlStr = $('<td></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        } else {
                            htmlStr = $('<td class="selected"></td>');
                            htmlStr.append('<a   id=' + item.dateStr + 
                              ' class="dt"><h3>' + item.dtDay + '</h3> </a>');
                        }
                        week6.append(htmlStr);
                    }
                });
                $("#component-table").trigger("update");
            } else {
                alertMsg('Oops, errors occur in retrieving calender');
            }
        }
    });
});

The final output has to be style by using css and probably assign in html attribute as well. The main challenges of creating own custom calendar/datepicker include calculate week number correctly, maps dates to day and week, ajax update the calendar according to month selected. Note: to do notification in the same tile for each day, you might need to add in more service logic in getCalendar function, and modify each row of week in html to display the items.

Points of Interest

The main point we would like to stress on is that building a custom calendar/datepicker using C# and jquery isn’t hard. It adds uniqueness to your application and our method can be applied in building appointment/schedule application. For example, we make our calendar a tile design with data and day are positioned on the same tile, you can add notification message on any tile based on your scenario.

Credit

Thanks to Mr Dwee Chee Siong(Intern) and Mr Kenovan Cheok(Back-End Engineer) from RabbitChemo team in compiling all required information for open source contribution.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here