Introduction
In this article I will show a new web control, a DataGrid with a HtmlSelect control in each column which allows a filter selection of items, like Microsoft Excel AutoFilter does. The control is directly inherited from System.Web.UI.WebControls.DataGrid
and was tested under Microsoft IE 6.0, Mozilla FireFox 1.0.2, Netscape 7.2, Opera 7.54.
Using the code
Add the DataGridAF.dll to your project�s references and toolbox, and use it as a DataGrid base control. The only difference is a new property, ShowFilter
, which allows you to show or hide the HtmlSelect controls in the columns header.
To run the demo project, unzip it under a virtual directory and start it. In the demo project, DataGridAF data are loaded from a xml file, Customers.xml (a select from Customers in Northwind database), so you don�t need to connect to a database.
The DataGridAutoFilter Class
DataGridAutoFilter class is very easy. All we need is to override two methods: OnItemDataBound
and RaisePostBackEvent
. The first is raised after each item is data bound to the DataGrid control and is where HtmlSelect controls are loaded (thank to Tony Truong's article for the idea); the second is used to capture the post back event raised when a HtmlSelect element is selected and is inherited from the IPostBackEventHandler interface (see this David S. Platt's book for a complete description).
OnItemDataBound Method
In this method all DataGrid items are data bound. First ListItemType.Header
, then ListItemType.Item, ...
, last ListItemType.Footer
.
override protected void OnItemDataBound(DataGridItemEventArgs e)
{
switch (e.Item.ItemType)
{
case ListItemType.Header:
break;
case ListItemType.Item:
case ListItemType.AlternatingItem:
case ListItemType.SelectedItem:
break;
case ListItemType.Footer:
break;
}
base.OnItemDataBound(e);
}
First Header: here is where the HtmlSelect is created, with �onchange� handler that causes a postback, it is added to an ArrayList to be accessed later on and it is added to the column header cell to be rendered. Also a SortedList object is created, but only to sort data before load in HtmlSelect.
case ListItemType.Header:
for(int i=0; i< e.Item.Cells.Count; i++)
{
HtmlSelect select = new HtmlSelect();
select.Attributes.Add("onchange", Page.GetPostBackEventReference(this));
e.Item.Cells[i].Controls.Add(select);
list.Add(select);
SortedList sorted = new SortedList();
sorted.Add("", "");
sort.Add(sorted);
}
break;
Then items are data bound: here SortedList are loaded with �distinct� values of the columns.
case ListItemType.Item:
case ListItemType.AlternatingItem:
case ListItemType.SelectedItem:
for(int i=0; i< e.Item.Cells.Count; i++)
{
SortedList sorted = (SortedList)sort[i];
if (sorted.ContainsValue(e.Item.Cells[i].Text) == false)
sorted.Add(e.Item.Cells[i].Text, e.Item.Cells[i].Text);
}
break;
Last the footer: here the sorted data are copied from the SortedList objects to the HtmlSelect controls.
case ListItemType.Footer:
for(int i=0; i< e.Item.Cells.Count; i++)
{
SortedList sorted = (SortedList)sort[i];
HtmlSelect select = (HtmlSelect)list[i];
for(int j=0; j< sorted.Count; j++)
select.Items.Add(sorted.GetByIndex(j).ToString());
sorted.Clear();
}
sort.Clear();
break;
RaisePostBackEvent Method
Here is were each DataGrid item is shown or hidden, according to the user selections.
public void RaisePostBackEvent(string eventArgument)
{
for (int i=0; i< Items.Count; i++)
{
for (int j=0; j< Items[i].Cells.Count; j++)
{
HtmlSelect select = (HtmlSelect)list[j];
if (select.SelectedIndex > 0)
{
if (Items[i].Cells[j].Text != select.Items[select.SelectedIndex].Text)
Items[i].Visible = false;
}
}
}
}
Conclusion
When you have many rows in a DataGrid and you wish to select a subset of data, an AutoFilter feature may be a solution. I have not found such a control on the web, so I tried to create it. DataGridAF is not a sophisticated control, and does not allow advanced selections like Excel AutoFilter does, but it is easy to use and you can also do multiple selections, wich can be combined with sorting or paging data.