Click here to Skip to main content
15,180,707 members
Articles / Web Development / ASP.NET
Posted 31 May 2009


53 bookmarked

ToDo List using ASP.NET MVC

Rate me:
Please Sign up or sign in to vote.
4.18/5 (10 votes)
15 Sep 2009LGPL36 min read
Creating task list using MVC
Due to some technical problems with my current host, the live demo will not be working. However, the source-code is provided along with for your reference. Soon, I will update the links on my blog! Updated with D/b schema download file.


It has been quite a while since MVC has been released. In this article, I will sample out an application for managing tasks (to-do lists) using ASP.NET MVC. The first alpha version is compiled against native features of MVC so as to give a glimpse of the native features.
Future releases will have jQuery plug-in and complete DAL based ToDoList. You can watch a live demo and download the code here.

The list (Index action) of TodoItem has been updated using the jquery dataTable plug-in. Thanks to Matt Berseth, for iTunes theme for GridView which has been modified and adapted for the dataTable. The screenshot for the same is shown below:



Before we can start off with coding, get a hold of the MVC pattern and download the VS 2008 extras from ASP.NET. You can get a kick-start from various videos and tutorials available from ASP.NET website.

The previous versions, available here, just used native MVC, i.e., no Data layer was added to the project. Now, if you look at the source, you will see that the code is more organized (quite a few comments though!). But, the Controller doesn't even know what it is doing, just that it knows of entity (TaskItem). What's more to this update for this article, the Using Code section
is almost re-written all over according to this release.

Before proceeding further, let's have a look at what the solution has to offer! Below is a quick look at the Data project.


Using the Code

The entity has been renamed to TaskItem from ToDoItem, though the controller name has been kept the same so as to keep the URLs the same. Let's have a look at the code from our entity class in Model.

public partial class TaskItem
  public int ItemId { get; set; }
  public string Title { get; set; }
  public int Priority { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime? DueDate { get; set; }
  public int? PercentCompleted { get; set; }
  public bool? TrackProgress { get; set; }
  public bool? RemindOnDelay { get; set; }

  #region object overrides
  public override bool Equals(object obj)
    if (obj is TaskItem)
      TaskItem item = (TaskItem)obj;
      return item.ItemId == this.ItemId;

    return base.Equals(obj);

  public override string ToString()
    return this.Title;

  public override int GetHashCode()
    return this.ItemId.GetHashCode();

The credit for this IQuerable pattern implementation goes to Rob Conery. If you want a more detailed tutorial on IQueryable and the pattern used for todolist, Rob has got video tutorials published on his blog for MVC Storefront application. You can watch them here.

To this partial class, we add validation logic so that there is no more validation on the Controller side, rather everything is accomplished at the Data layer only. The following code checks for any violations (validation errors) in the code.

public partial class TaskItem
  public bool IsValid
    get { return (GetRuleViolations().Count() == 0); }

  public IEnumerable<RuleViolation> GetRuleViolations()
    if (string.IsNullOrEmpty(Title))
      yield return new RuleViolation("Task title is required.", "Title");

    if (Priority <= 0 || Priority > 3)
      yield return new RuleViolation("Priority must lie between 1 to 3.", "Priority");

    if (DueDate != null && DueDate < StartDate)
      yield return new RuleViolation("Due date must be greater than start date.",
      "Due Date");

    if (PercentCompleted != null && (PercentCompleted <= 0 || PercentCompleted > 100))
      yield return new RuleViolation
      ("Please specify Percentage completed between 1 to 100.",
          "Percentage Completed");

    yield break;

The function GetRuleViolations will help us adding validation checks for the entity operations in the data layer. Thanks to Scott Guthrie et. al. for authoring Professional ASP.NET MVC (a great book!).

We can check for validation errors in operations very easily as shown in the InsertTaskItem function below:

/// <summary>
/// Insert the param to database.
/// </summary>
/// <param name="item">TaskItem object to be inserted.</param>
public void InsertTaskItem(TaskItem item)
  if (!item.IsValid)
    throw new ArgumentException();
  using (LinqTaskItemDataContext db = new LinqTaskItemDataContext())
    SqlRepository.TaskItem dbItem = db.TaskItems
      .Where(x => x.ItemID == item.ItemId)
    bool isNew = false;
    if (dbItem == null)
      dbItem = new DevOrigin.ToDoList.Data.SqlRepository.TaskItem();
      isNew = true;
    int itemId = (from taskItem in db.TaskItems
                  select taskItem.ItemID).Max();
    //add the item
    dbItem.ItemID = ++itemId;
    dbItem.Title = item.Title;
    dbItem.Priority = item.Priority;
    dbItem.StartDate = item.StartDate;
    dbItem.DueDate = item.DueDate;
    dbItem.PercentCompleted = item.PercentCompleted;
    dbItem.TrackProgress = item.TrackProgress;
    dbItem.RemindOnDelay = item.RemindOnDelay;
    if (isNew)

Now, the Service layer project just implements ITaskItemService interface. The Service class uses the ITaskItemRepository methods to provide services to its user.

As you can see in Controller methods, the code has just reduced to a matter of one or two lines. All the implementation is hidden in the Data layer and thus can easily be changed without affecting the layers working over it until the output remains the same.

Oh, did I forget to mention how to get that pretty look for data in Index controller. That's easy, huh! I used dataTable jQuery plug-in to get that thing and then a bit of styling to make it look pretty. Let's look at the code in Index view for dataTable.

<script type="text/javascript" charset="utf-8">
    var oTable;
    var giRedraw = false;
    $(document).ready(function() {
      /* Add a click handler to the rows - this could be used as a callback */
      $("#taskList tbody").click(function(event) {
        $(oTable.fnSettings().aoData).each(function() {

      oTable = $('#taskList').dataTable({
        "bAutoWidth": false,
        "bFilter": false

    /* Get the rows which are currently selected */
    function fnGetSelected(oTableLocal) {
      var aReturn = new Array();
      var aTrs = oTableLocal.fnGetNodes();

      for (var i = 0; i < aTrs.length; i++) {
        if ($(aTrs[i]).hasClass('row_selected')) {
      return aReturn;

The highlighted code will actually render the dataTable in the View. Also, you will notice that a handler has been registered which is used to select the row when the user clicks on any row inside the table. And there is an unused function which is for future use, fnGetSelected which returns the selected row. You can get a full reference of dataTable in the accompanying website.

The ViewModel Pattern

Now, let's come to the ViewModel pattern which is required for managing Priority column. Before, I can discuss some part of the code, let me take you through the newly added code.

public class TaskItemViewModel
  public TaskItem TaskItem { get; private set; }
  public SelectList PriorityList { get; private set; }

  private List<Priority> Priorities = new List<Priority>();

  private void Initialize()
    Priorities.Add(new Priority { Id = 1, Name = "General" });
    Priorities.Add(new Priority { Id = 2, Name = "Urgent" });
    Priorities.Add(new Priority { Id = 3, Name = "Critical" });

  public string GetPriorityByName(int index)
    foreach (Priority priority in Priorities)
      if (priority.Id == index)
        return priority.Name;

    return null;

  public int GetPriorityById(int index)
    foreach (Priority priority in Priorities)
      if (priority.Id == index)
        return priority.Id;

    return 0;

  public TaskItemViewModel(TaskItem taskItem, int selectedIndex)
    //Initialize the priorities list.

    TaskItem = taskItem;
    PriorityList =
      new SelectList(Priorities, "Id", "Name", selectedIndex);

  class Priority
    public int Id { get; set; }
    public string Name { get; set; }

Again, the credit for understanding this pattern goes to the great book of Scott Gu et. al. (Professional ASP.NET MVC).

What I've done is initialized the TaskItemViewModel class with the TaskItem object and the Priority values and then based on the selected index, the SelectList is returned with the help of property. Two helper methods, GetPriorityByName and GetPriorityById are also provided. Let's see the usage of the ViewModel class in the controller. Consider the Edit action as an example:

//Edit Action: GET Method
TaskItem taskItem = _taskItemSvc.GetItem(id);
return View(new TaskItemViewModel(taskItem, taskItem.Priority));

//Edit Action: POST Method
TaskItem itemToUpdate = new TaskItem();

  UpdateModel(itemToUpdate, collection.AllKeys);

  return RedirectToAction("Index");
  foreach (var issue in itemToUpdate.GetRuleViolations())
    ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);

  TaskItem taskItem = _taskItemSvc.GetItem(id);
  return View(new TaskItemViewModel(taskItem, taskItem.Priority));

Now, the priorities are named in Index action too using the same ViewModel class. But what we are left with that there is no way we can set the task's status to Done!

Before setting the item's status as "done", we need to move that item to the trash list so that we can easily manage the trashed task items and active items. So, all I have done is a few modifications in the Data layer to include a new Model, modifications in Service layer to call the Data layer functions. And finally, we have the accomplish action which implements all the stuff. Here is the code for the controller action:

// GET Method
public ActionResult Accomplish(int id)
  TaskItem taskItem = _taskItemSvc.GetItem(id);
  return View(new TrashedTaskItemViewModel(taskItem, taskItem.Priority));

// POST Method
public ActionResult Accomplish(int id, string confirmButton)
    return RedirectToAction("Index");
    TaskItem taskItem = _taskItemSvc.GetItem(id);
    return View(new TrashedTaskItemViewModel(taskItem, taskItem.Priority));

As you can see, I've created a ViewModel class for TrashedTaskItem too to manage View.

<%if (item.TaskItem.IsAccomplished == true)
  Response.Write("<tr class='accomplished'>");
<%if (item.TaskItem.IsAccomplished == true)
{ %>
  <% Response.Write("done"); %>
{ %>
  <%= Html.ActionLink("accomplish", "Accomplish", 
		new { id = item.TaskItem.ItemId })%>
<%} %>
| <%= Html.ActionLink("edit", "Edit", new { id = item.TaskItem.ItemId })%>

The above code has been taken from Index action. It adds a class accomplished to the table-row if the status of task-item is set as done and if not, the accomplish link is shown otherwise.

When you try to create a new task, you get a list of items that you need to enter which are: Title, Priority, Start Date, Due Date, % Completed, Track Progress, and Show Reminder.

Interestingly, "Show Reminder" or ReminderOnDelay, will send out an e-mail to the owner of the task automatically if it lags behind by a given set of configuration criteria though it is not implemented, but it can easily be done. And ToDoList will show you how to do that.

The next plan is to add enhancements and remaining functionality to the todolist.


Once you download the code from the website, you will find schema.sql file with the help of which you will be able to configure the database. After the database is configured, update the web.config file so that the connection string is appropriately reflected.


  • (alpha): Fixed Index action, added the trash task-item functionality which maintains the accomplishment status of the task
  • (alpha): Fixed taskitem-priority with implementation of ViewModel pattern
  • 0.1.3 (alpha): Updated with Repository and Queryable pattern. Details of Repository and Queryable pattern are available on MSDN, Scott Gu's blog, Rob Corney's blog
  • v0.1.2 (alpha): Updated jQuery plugs for Detail action
  • v0.1 (alpha): Initial release


This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


About the Author

A. Bhandari a.k.a Amit Bhandari
Software Developer (Senior)
India India
I: working as software engineer.
My Blog:

Comments and Discussions

GeneralMy vote of 1 Pin
mrkt27-Dec-12 7:22
Membermrkt27-Dec-12 7:22 
GeneralMy vote of 1 Pin
AbdullaMohammad10-Jun-12 11:04
MemberAbdullaMohammad10-Jun-12 11:04 
GeneralProblems & Solutions. Pin
Member 31638983-Aug-10 12:50
MemberMember 31638983-Aug-10 12:50 
GeneralSchema.sql missing from download zip Pin
nick crowther14-Sep-09 11:14
Membernick crowther14-Sep-09 11:14 
AnswerRe: Schema.sql missing from download zip Pin
A. Bhandari a.k.a Amit Bhandari15-Sep-09 17:45
MemberA. Bhandari a.k.a Amit Bhandari15-Sep-09 17:45 
Generalgood article Pin
Donsw25-Jul-09 6:58
MemberDonsw25-Jul-09 6:58 
GeneralSource download Pin
Smitha Nishant31-May-09 12:18
protectorSmitha Nishant31-May-09 12:18 
GeneralRe: Source download Pin
A. Bhandari a.k.a Amit Bhandari31-May-09 20:25
MemberA. Bhandari a.k.a Amit Bhandari31-May-09 20:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.