in Search
 
Home Blogs Forums Marketplace Files
 
 
 

Moving From One Task To The Next...

Search Your TaskList By Date

In <my last post/> I explained how you might add a search bar to your TaskList. I'm going to follow up with another sample that demonstrates how to build a search bar that selects tasks by date. To make selecting a date for my sample easy to do, I've decided to use <The Coolest DHTML / JavaScript Calendar/> that I could find.

Search By Date

The first thing I’m going to do is add the following ASP.NET controls to the form tag within my web page.

<div style="margin: 6px 0;">
<asp:dropdownlist id="columnsDropDownList" runat="server" />
<asp:dropdownlist id="commandsDropDownList" runat="server" />
<asp:textbox id="date" runat="server" />
<script type="text/javascript">
Calendar.setup({
inputField : "date", // id of the input field
ifFormat : "%m/%d/%Y", // format of the input field
showsTime : false,
singleClick : true
});
</script>
<asp:button id="searchButton" runat="server" text="Search" />
</div>

I am going to use the DropDownList controls to display the columns and operators the user selects to match the date they pick from the popup calendar associated with the TextBox control. The Button control is used to kick off the search.

To get the Calendar control to work, you need to add the following lines to the head tag.

<script type="text/javascript" src="calendar/calendar.js"></script>
<script type="text/javascript" src="calendar/calendar-setup.js"></script>
<script type="text/javascript" src="calendar/lang/calendar-en.js"></script>
<link rel="stylesheet" type="text/css" media="all" href="calendar/skins/aqua/theme.css" />

Next, I'm going to add a GridView control to display the results of my search.

<asp:gridview id="gridView" autogeneratecolumns="False"
emptydatatext="Sorry, no matching records were found." runat="server">
<columns>
<asp:boundfield datafield="ProcessName" headertext="Process" readonly="True"/>
<asp:boundfield datafield="TaskName" headertext="Task" readonly="True"/>
<asp:boundfield datafield="ResponsibleName" headertext="Responsible" readonly="True"/>
<asp:boundfield datafield="TaskStatus" headertext="Status" readonly="True"/>
<asp:boundfield datafield="TaskStartDate" headertext="Start Date"
htmlencode="False" dataformatstring="{0:d}" readonly="True"/>
<asp:boundfield datafield="TaskDueDate" headertext="Due Date"
htmlencode="False" dataformatstring="{0:d}" readonly="True"/>
</columns>
</asp:gridview>

With the controls in place, it's time to start adding some code. The first thing I'm going to do is build a DateColumnDecorator class that implements the IDateColumn interface.

using System;
using Teamplate.BLL.Data;
public class DateColumnDecorator : IDateColumn
{
private readonly string displayName;
private readonly IDateColumn column;
public DateColumnDecorator(string displayName, IDateColumn column)
{
this.displayName = displayName;
this.column = column;
}

public override string ToString()
{
return displayName;
}

public string ColumnName
{
get { return column.ColumnName; }
}
public ICriteria GreaterThan(DateTime value)
{
return column.GreaterThan(value);
}
// more methods }

The primary purpose of the decorator class is to wrap another IDateColumn instance in order to override the ToString method. By default the ToString method returns the value of the ColumnName property. Although useful, this is not what I want to display to the user in the DropDownList control. All of the other methods on the decorator simply delegate to the IDateColumn instance being wrapped.

The DateColumnDecorator is put to work in the Columns property as seen below.

private IList<IDateColumn> columns;
private IList<IDateColumn> Columns
{
get
{
if (columns == null)
{
columns = new List<IDateColumn>();
columns.Add(new DateColumnDecorator("Due Date", TaskList.TaskDueDate));
columns.Add(new DateColumnDecorator("Start Date", TaskList.TaskStartDate));
}
return columns;
}
}

My second DropDownList control allows the user to select an operator that describes how they want to compare against the selected date. The goal here is to translate several unique method signatures into one general purpose signature. The easiest way I could think of to do this was to introduce a command pattern.

I'm going to start with the following abstract class whose primary responsibility is to handle the display name functionality.

using System;
using Teamplate.BLL.Data;

public abstract class DateCommand
{
private readonly string displayName;

public DateCommand(string displayName)
{
this.displayName = displayName;
}

public override string ToString()
{
return displayName;
}

public abstract ICriteria Execute(IDateColumn column, DateTime value);
}

For each operator that I want to appear in the DropDownList control, I am going to create a class that inherits from the abstract DateCommand class. Now all I need to do is override the Execute method and call the corresponding method on the IDateColumn instance being passed in to the Execute method.

using System;
using Teamplate.BLL.Data;

public class GreaterThan : DateCommand
{
public GreaterThan() : base(">")
{
}

public override ICriteria Execute(IDateColumn column, DateTime value)
{
return column.GreaterThan(value);
}
}

This well-known, although often overlooked, technique is called double-dispatch.

using System;
using Teamplate.BLL.Data;

public class GreaterThanOrEqualTo : DateCommand
{
public GreaterThanOrEqualTo() : base(">=")
{
}

public override ICriteria Execute(IDateColumn column, DateTime value)
{
return column.GreaterThanOrEqualTo(value);
}
}

The command objects adapt each call on a unique method signature of the IDateColumn instance into a general purpose method called Execute. When I add a Commands property to my code behind, all of my command objects are added to a list of type DateCommand. Nothing like a little polymorphism to spice things up.

private IList<DateCommand> commands;
private IList<DateCommand> Commands
{
get
{
if (commands == null)
{
commands = new List<DateCommand>();
commands.Add(new GreaterThan());
commands.Add(new GreaterThanOrEqualTo());
commands.Add(new EqualTo());
commands.Add(new LessThanOrEqualTo());
commands.Add(new LessThan());
}
return commands;
}
}

With everything in its place, it's now time to hookup our event handlers, load the DropDownList controls and of course obtain a new session token.

private string sessionToken;
private int userId;

protected void Page_Load(object sender, EventArgs e)
{
BSession bSession = new BSession();
bSession.Connect(string.Empty, string.Empty, string.Empty);
sessionToken = bSession.GetToken();
userId = bSession.GetUserId();

LoadControls();
HookupEventHandlers();
}

private void HookupEventHandlers()
{
searchButton.Click += delegate { FindMatchingTaskRecords(); };
}

private void LoadControls()
{
if (!IsPostBack)
{
columnsDropDownList.DataSource = Columns;
columnsDropDownList.DataBind();

commandsDropDownList.DataSource = Commands;
commandsDropDownList.DataBind();
}
}

And finally, it's time to bring the new BQueryRuntime API into play. Pay attention to the SearchCriteria property as that's where all of the magic takes place.

private ICriteria SearchCriteria
{
get
{
IDateColumn selectedColumn = Columns[columnsDropDownList.SelectedIndex];
DateCommand selectedCommand = Commands[commandsDropDownList.SelectedIndex];

return selectedCommand.Execute(selectedColumn, DateTime.Parse(date.Text));
}
}

private void FindMatchingTaskRecords()
{
BQueryRuntime runtime = new BQueryRuntime();
runtime.SetSessionToken(sessionToken);

IQuery query = runtime.CreateQuery(QueryType.TaskList);
query.AddCriteria(
TaskList.TaskStatus.MatchAny(TaskStatus.Ready | TaskStatus.Waiting)
.And(TaskList.ResponsibleId.EqualTo(userId))
.And(SearchCriteria));

gridView.DataSource = runtime.ExecuteDataSet(query);
gridView.DataBind();
}

That's it. Feel free to download the complete project <here/>. If you like what you see, or even if you don't, I'm open to comments.

Published Thursday, June 14, 2007 9:23 AM by DonCampbell
Filed Under: ,

Comments

No Comments
Anonymous comments are disabled

This Blog

Post Calendar

<June 2007>
SuMoTuWeThFrSa
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

Post Categories

Syndication

  Privacy    Site Terms   Contact Administrator