Quick Filter - AJAX tutorial for .NET developers
Creating an AJAX data grid with user-friendly and interactive search and filtering capabilities is just a matter of a few minutes if you're using Ajaxium.
A recurring task in software development is to display tabular data. Both the ASP.NET 1.0 DataGrid control and its successor, the GridView control, introduced in the ASP.NET 2.0 allow you to display and manage tabular data in a grid. Usually each row represents one record in the data source. To help the user navigate in the large amount of records these controls provide a sorting and paging functionality. However, if a large number of records is displayed, it is hard for the user to navigate among them and a user-friendly search functionality becomes essentially important.
For desktop applications it's a usual thing to provide interactive search or filtering capabilities, whereas in web applications the search functionality is customarily implemented in an old manner - with a complete reload of the document. So interactive filtering is one of the features which, being easily implemented with the aid of Ajaxium, greatly impresses visitors.
This tutorial starts with placing and setting up the data-bound GridView control, then goes on to add an old-style filtering capability, and after that Ajax-es your page with the help of Ajaxium.
Note: ASP.NET 1.0 examples use the DataGrid control (see the downloads section below).
Step 1: Adding data-bound GridView control with the paging enabled
Keeping in mind that we want to enable filtering later, let's place an ObjectDataSource data source control onto the form. Real-life applications can use AccessDataSource or SqlDataSource controls, which are also support filter expressions.
We configure the control to load the data from a very simple business layer (shown below) so the tag will look as follows:
<asp:ObjectDataSource ID="ObjectDataSource1"
SelectMethod="GetData" TypeName="BizLogic">
</asp:ObjectDataSource>
FILE: BizLogic.cs (simplest business layer possible)
public class BizLogic
{
public static DataTable GetData()
{
DataTable booksTable = new DataTable("Books");
booksTable.Columns.Add("id", typeof(int));
booksTable.Columns.Add("title", typeof(string));
booksTable.Columns.Add("author", typeof(string));
booksTable.Rows.Add(1, "Pure JavaScript", "Wyke, Gilliam, and Ting");
booksTable.Rows.Add(2, "Effective C++: 50 Specific Ways to Improve Your Programs and Designs",
"Scott Meyers");
booksTable.Rows.Add(3, "Assembly Language: Step-By-Step", "Jeff Duntemann");
booksTable.Rows.Add(4, "Oracle PL/SQL Best Practices", "Steven Feuerstein");
booksTable.Rows.Add(5, "Counter Hack: A Step-by-Step Guide to Computer Attacks and Effective Defenses",
"Ed Skoudis");
booksTable.Rows.Add(6, "Mastering Regular Expressions, Second Edition", "Jeffery E. F. Friedl");
booksTable.Rows.Add(7, "Regular Expressions in .NET", "Michael Weinhardt, Chris Sells");
return booksTable;
}
}
Then let's place the GridView control onto the form and bind it to the ObjectDataSource1 control.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="id"
DataSourceID="ObjectDataSource1" Width="100%" BackColor="LightGoldenrodYellow" BorderColor="Tan"
BorderWidth="1px" CellPadding="2" ForeColor="Black" GridLines="None" AllowPaging="True"
AllowSorting="true" PageSize="5">
<Columns>
<asp:TemplateField HeaderText="Title" SortExpression="title">
<ItemTemplate>
<asp:Label ID="TitleLabel" Runat="Server" Text='<%# Eval("title") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Author" SortExpression="author">
<ItemTemplate>
<asp:Label ID="AuthorLabel" Runat="Server" Text='<%# Eval("author") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<FooterStyle BackColor="Tan" />
<SelectedRowStyle BackColor="DarkSlateBlue" ForeColor="GhostWhite" />
<PagerStyle BackColor="PaleGoldenrod" ForeColor="DarkSlateBlue" HorizontalAlign="Center" />
<HeaderStyle BackColor="Tan" Font-Bold="True" />
<AlternatingRowStyle BackColor="PaleGoldenrod" />
</asp:GridView>
Please note that we have used two TemplateField fields instead of BoundFields for data binding. The TemplateField will allow us to highlight the text entered in the search box and found in the data records.
Step 2: Implementing data filtering and highlighting
Now we are ready to enable filtering. To do it, we need to place a TextBox control with AutoPostBack enabled and write the TextChanged event handler. Thanks to the power of the .NET filtering can be implemented with ease and, even being prettily formatted, would take no more than 10 lines of code:
protected void searchBox_TextChanged(object sender, EventArgs e)
{
ApplyFilter();
}
private void ApplyFilter()
{
if(searchBox.Text.Trim().Length > 0)
ObjectDataSource1.FilterExpression =
string.Format("(title LIKE '*{0}*') OR (author LIKE '*{0}*')", searchBox.Text);
else
ObjectDataSource1.FilterExpression = string.Empty;
GridView1.DataBind();
}
Highlighting is also important because it allows the user to quickly browse search results. Shown below is an easy highlighting function which changes the background color of the search word in the given string. We apply this function to both columns of the grid view during the data binding step as follows:
<asp:TemplateField HeaderText="Title" SortExpression="title">
<ItemTemplate>
<asp:Label ID="TitleLabel" Runat="Server" Text='<%# Highlight(Eval("title")) %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Author" SortExpression="author">
<ItemTemplate>
<asp:Label ID="AuthorLabel" Runat="Server" Text='<%# Highlight(Eval("author")) %>' />
</ItemTemplate>
</asp:TemplateField>
Now you can even test the page to see how the filtering and highlighting work. When the user enters any text into the text box and presses 'enter' or clicks outside the text box - a postback occurs, and after the page reload the user can see the grid with only records which contain the search text. It's easy in use, but the page reload makes the filtering ugly and inconvenient. Nothing doing, the web 1.0 approach can't give you more than it has. And now it's time for Step 3.
Step 3: Enabling AJAX
The easiest way to enable AJAX in our project is to complete a few simple steps described in the Integration via Aggregation topic. Right after that you can test the page with AJAX enabled. However, the Intelligent Performance Advisor will tell you that it is a good idea to add some panels onto the page to optimize the client-side performance. We add two panels. The first one wraps the GridView1 control, so all the changes in the grid will be applied by that panel. And the second panel wraps the search text box. We will use it later.
<asp:Panel id="Panel2" runat="server"
AjaxiumUpdateable="false">
<asp:TextBox id="searchBox" runat="server" width="98%"
OnTextChanged="searchBox_TextChanged" ></asp:TextBox>
</asp:Panel>
<asp:Panel id="Panel1" runat="server">
<asp:GridView ID="GridView1"
*****
</asp:GridView>
</asp:Panel>
Step 4: Making the filtering reactive
You can notice how much AJAX has improved our simple grid. However, it is not very convenient as yet because the user has to apply the filter by clicking the mouse or by pressing 'enter' during input. To make the filtering real-time we need to apply the filter each time the user finishes or just stops entering text into the search text box for a moment. This can be achieved with the help of two JavaScript functions. The first one simply launches the timer. The second one is called periodically by the timer and checks if the text in the search text box has been changed by the user during the timeout interval. If that is the case, the function performs a postback (automatically transformed into an AJAX request by Ajaxium). As a result of the AJAX postback the filter will be applied and the data grid will display relevant records. Both functions are shown below:
<script language="javascript" type="text/javascript">
var highlightedText = "";
var timerId = -1;
var counter = 0;
function trackChanges(p_textBoxId, p_postBackScript)
{
var l_elem = Ajaxium_GetElementById(p_textBoxId);
if(l_elem)
{
var currentText = l_elem.value;
if(highlightedText != currentText)
{
highlightedText = currentText;
counter = 1;
} else
{
if(counter == 1)
{
counter = 0;
eval(p_postBackScript);
}
}
}
}
function startTrackChanges(p_textBoxId, p_postBackScript)
{
if(timerId == -1)
{
timerId = setInterval("trackChanges(\""+p_textBoxId+"\", \""+p_postBackScript+"\");", 300);
}
}
</script>
The startTrackChanges function is called from the onkeydown event handler of the search box:
protected void Page_Load(object sender, EventArgs e)
{
searchBox.Attributes["onkeydown"] =
string.Format(@"startTrackChanges(""{0}"",
""{1}"");", searchBox.ClientID,
GetPostBackEventReference(searchBox));
}
Since our implementation is more AJAX-friendly we now disable the default auto postback functionality of the search box by setting AutoPostBack property to false.
Note 1: Why does the second panel need to be non-updateable?
If you play with our brand new AJAX data grid with filtering, you will notice that there is still something wrong. If changes to the content apply just while you're entering the text into the search box, some characters may apparently disappear. This occurs because the text box control not only receives the value entered by the user into the field, but also updates itself. When the AJAX update is applied to the page, the search box is updated as well with the value sent to the server a few milliseconds before. And all the characters entered during these milliseconds can be lost.
Well, it's not a bug in itself but we still need to get rid of it. By setting the AjaxiumUpdateable property of the Panel which contains the search box to false, we disable all AJAX updates of the search box, so updates will not affect it anymore.
Note 2: Make sure that filtering is applied
Since the user is able to enter the text into the search box during an AJAX update of the page content, we must make sure that the filter applied to the data grid has used the latest search text entered. The easiest way to detect the changes between the entered text and the text used for filtering is to add the following line to the end of the searchBox_TextChanged method:
RegisterAjaxUpdateScript("QuickFilter.highlightedText",
string.Format("highlightedText
= \"{0}\";", searchBox.Text));
This statement will set the highlightedText variable to the recent value used for filtering after AJAX update is completed on the client. So the trackChanges function will be able to compare this value and initiate another update if necessary.
Done
As you can see we needed just a few minutes not only to make the ASP.NET GridView control AJAX-enabled but also to add reactive filtering capabilities to it. This is yet another proof that Ajaxium has succeeded in its mission - that of providing unbeatable speedup in the AJAX applications development.
Download full source code of the sample
ASP.NET 2.0 (C#)
ASP.NET 2.0 (Visual Basic .NET)
ASP.NET 1.1 (C#)
ASP.NET 1.1 (Visaul Basic .NET)
Note: The source code is provided solely for education purposes. The code will
neither compile nor run unless at least an Evaluation Edition of the Ajaxium
server-side component is used.
Live demo
See this AJAX-enabled GridView in action