RadGrid for ASP.NET

Filtering with MS DropDownList instead of textbox Send comments on this topic.
Filtering > How to > Filtering with MS DropDownList instead of textbox

Glossary Item Box

In some cases you may whish to display the available options when the user choose an option from a dropdown list instead of textbox in grid column header. Then you may want choose an option and filter the record on item selection/Enter key press. 
This behavior is easily attainable by replacing the default textbox input with MS DropDownList instance. Here are the steps you need to undertake in order to configure the grid as mentioned above:

  • extend the default GridBoundColumn to remove the default textbox and embed MS DropDownList inside the filtering cell
  • configure the options for the dropdown list control as per your requirements (the essential parts are to set AutoPostBack = true, ID for the dropdown, binding the combobox items and wiring the SelectedIndexChanged event). These operation should take place in the overriden SetupFilterControls(TableCell cell) method
  • override the SetCurrentFilterValueToControl(TableCell cell) / GetCurrentFilterValueFromControl(TableCell cell) methods to set/get the dropdown list SelectedValue.
  • raise command event for the GridFilteringItem calling its FireCommandEvent(commandName, new Pair(filterFunctionName, columnUniqueName)) method

The example below is expanded by enabling the AJAX mechanism of the grid along with its built-in paging feature. The filtering action will be triggered when you choose item from the dropdown list filter or highlight list item and press Enter from the keyboard.
In addition, there is Clear filter button on the page which restores the initial grid content.

Note that when using data source controls (part of ASP.NET 2.0) the NeedDataSource event of the grid will not be fired. An online demo presenting similar approach with template column is available here.
 The sample is enhanced with additional method which selects only distinct records for the dropdown list in the column header (useful when there are duplicate field values in the column source). Please note that we store the SelectedValue and the FilteredColumnName for the item clicked by the user in Session variables. Then we retrieve these values and filter the items in the dropdown filters (according to the current filter applied) inside the GetDropDownListItems method.

MainPage.aspx
ASPX/ASCX Copy Code
<rad:RadGrid ID="RadGrid1" Width="200px" AllowFilteringByColumn="True" AllowSorting="True" EnableAJAX="true" EnableAJAXLoadingTemplate="true"
 
AllowPaging="True" runat="server" OnColumnCreating="RadGrid1_ColumnCreating" AutoGenerateColumns="False">
</
rad:RadGrid>
<
asp:Button id="clrFilters" runat="server" Text="Clear filters" OnClick="clrFilters_Click" />

 

C# Copy Code
MainPage.cs

using System;
using System.Data;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Telerik.WebControls;
using System.Data.OleDb;

   
public partial class TestFilter : System.Web.UI.Page
   {
        
private DataSet ds;
        
private DataSet dsTemp;

       
protected void Page_Load(object sender, EventArgs e)
       {

       }

        
private void InitializeComponent()
       {
           
this.RadGrid1.NeedDataSource += new Telerik.WebControls.GridNeedDataSourceEventHandler(this.RadGrid1_NeedDataSource);
           
this.Load += new System.EventHandler(this.Page_Load);
       }

       
override protected void OnInit(EventArgs e)
       {

           InitializeComponent();
           
base.OnInit(e);
       }


        
public DataTable SelectDistinct(string TableName, DataTable SourceTable, string FieldName)
       {
           DataTable dt =
new DataTable(TableName);
           dt.Columns.Add(FieldName, SourceTable.Columns[FieldName].DataType);

            
object LastValue = null;

           
foreach (DataRow dr in SourceTable.Select("", FieldName))
           {
                
if (LastValue == null || !(ColumnEqual(LastValue, dr[FieldName])))
               {
                   LastValue = dr[FieldName];
                   dt.Rows.Add(
new object[] { LastValue });
               }
           }
            
if (dsTemp != null)
               dsTemp.Tables.Add(dt);
            
return dt;
       }

        
private bool ColumnEqual(object A, object B)
       {

           
// Compares two values to see if they are equal. Also compares DBNULL.Value.
           
// Note: If your DataTable contains object fields, then you must extend this
           
// function to handle them in a meaningful way if you intend to group on them.

            
if (A == DBNull.Value && B == DBNull.Value) // both are DBNull.Value
                
return true;
            
if (A == DBNull.Value || B == DBNull.Value) // only one is DBNull.Value
                
return false;
            
return (A.Equals(B)); // value type standard comparison
       }



       
private DataView GetDropDownListItems(DataSet SourceDS, string ColumnName, string pattern, string filteredColumnName)
       {
           DataSet dsColumn =
new DataSet(ColumnName);
           dsColumn.Tables.Add(ColumnName);
           dsColumn.Tables[0].Columns.Add(ColumnName, SourceDS.Tables[0].Columns[ColumnName].DataType);
           
foreach (DataRow row in SourceDS.Tables[0].Rows)
           {
               
if (pattern == null)
               {
                   dsColumn.Tables[0].Rows.Add(
new object[] { row[ColumnName] });
               }
               
else if ((string)row[filteredColumnName] == pattern)
               {
                   dsColumn.Tables[0].Rows.Add(
new object[] { row[ColumnName] });
               }
           }
           DataView dv = SelectDistinct(dsColumn.Tables[0].TableName, dsColumn.Tables[0], ColumnName).DefaultView;
           dv.Sort = ColumnName +
" ASC";
           
return dv;
       }

       
protected void RadGrid1_NeedDataSource(object source, GridNeedDataSourceEventArgs e)
       {
           ds = DataSourceHelperCS.GetDataSet(
"SELECT ContactName, City, Country FROM Customers");

           
this.RadGrid1.MasterTableView.Columns.Clear();


           
foreach (DataColumn dataColumn in ds.Tables[0].Columns)
           {
               RadGrid.MyCustomFilteringColumn gridColumn =
new RadGrid.MyCustomFilteringColumn();
               
this.RadGrid1.MasterTableView.Columns.Add(gridColumn);

               gridColumn.DataField = dataColumn.ColumnName;
               gridColumn.HeaderText = dataColumn.ColumnName;
               gridColumn.ListDataSource = GetDropDownListItems(ds, dataColumn.ColumnName, (
string)Session["SelectedValue"], (string)Session["FilteredColumnName"]);
           }

           
this.RadGrid1.DataSource = ds;
       }

       
protected void RadGrid1_ColumnCreating(object sender, GridColumnCreatingEventArgs e)
       {
            
if (e.Column == null)
           {
               e.Column =
new RadGrid.MyCustomFilteringColumn();
           }
       }
       
protected void clrFilters_Click(object sender, EventArgs e)
       {
           
foreach (GridColumn column in RadGrid1.MasterTableView.Columns)
           {
               column.CurrentFilterFunction = GridKnownFunction.NoFilter;
               column.CurrentFilterValue = String.Empty;
           }
           Session[
"SelectedValue"] = null;
           Session[
"FilteredColumnName"] = null;
           RadGrid1.MasterTableView.FilterExpression = String.Empty;
           RadGrid1.MasterTableView.Rebind();


       }
}

C# Copy Code
MyCustomFilteringColumn.cs

using System.Data;
using Telerik.WebControls;
using System.Web.UI.WebControls;
using System.Web.UI;
using System;
using System.Collections;

namespace RadGrid
{
   
public class MyCustomFilteringColumn : GridBoundColumn
   {
        
private object listDataSource = null;

       
//RadGrid will call this method when it initializes the controls inside the filtering item cells
       
protected override void SetupFilterControls(TableCell cell)
       {
           
base.SetupFilterControls(cell);
           cell.Controls.RemoveAt(0);

           System.Web.UI.WebControls.DropDownList list =
new System.Web.UI.WebControls.DropDownList();
           list.ID =
"list" + this.DataField;

           list.Height = Unit.Pixel(300);
           list.AutoPostBack = true;

           list.SelectedIndexChanged +=
new EventHandler(list_SelectedIndexChanged);

           cell.Controls.AddAt(0, list);
           cell.Controls.RemoveAt(1);

           list.DataTextField =
this.DataField;
           list.DataValueField =
this.DataField;

           list.DataSource =
this.ListDataSource;
           list.DataBind();

        }

       
void list_SelectedIndexChanged(object sender, EventArgs e)
       {
           GridFilteringItem filterItem = (sender
as DropDownList).NamingContainer as GridFilteringItem;

           
this.SelectedValue = (sender as DropDownList).SelectedValue;
           
this.FilteredColumnName = this.UniqueName;

            
if (this.UniqueName == "Index")
           {
               
//this is example for filtering for integer column type
               
filterItem.FireCommandEvent( "Filter", new Pair("EqualTo", this.UniqueName));
           }
           
//filtering for string column type
           
filterItem.FireCommandEvent( "Filter", new Pair("Contains", this.UniqueName));

       }
        
public object ListDataSource
       {
            get
           {
                
return this.listDataSource;
           }
            set
           {
               listDataSource = value;
           }
       }
        
public string SelectedValue
       {
            get
           {
                
object res = this.Owner.OwnerGrid.Page.Session["SelectedValue"];
                
if (res != null)
               {
                    
return (string)res;
               }
                
return "";
           }
            set
           {
               
this.Owner.OwnerGrid.Page.Session[ "SelectedValue"] = value;
           }
       }
       
public string FilteredColumnName
       {
           get
           {
               
object res = this.Owner.OwnerGrid.Page.Session["FilteredColumnName"];
               
if (res != null)
               {
                   
return (string)res;
               }
               
return "";
           }
           set
           {
               
this.Owner.OwnerGrid.Page.Session["FilteredColumnName"] = value;
           }
       }
       
//RadGrid will call this method when the value should be set to the filtering input control(s)
       
protected override void SetCurrentFilterValueToControl(TableCell cell)
       {
           
base.SetCurrentFilterValueToControl(cell);

           System.Web.UI.WebControls.DropDownList list = (System.Web.UI.WebControls.DropDownList)cell.Controls[0];
            
if (this.CurrentFilterValue != string.Empty)
           {
               list.SelectedValue =
this.CurrentFilterValue;
           }
       }

       
//RadGrid will call this method when the filtering value should be extracted from the filtering input control(s)
       
protected override string GetCurrentFilterValueFromControl(TableCell cell)
       {
           System.Web.UI.WebControls.DropDownList list = (System.Web.UI.WebControls.DropDownList)cell.Controls[0];

            
return list.SelectedValue;
       }
        
public override GridColumn Clone()
       {
            
return base.Clone();
       }
       
protected override string GetFilterDataField()
       {
            
return this.DataField;
       }

   }
}
VB.NET Copy Code
MainPage.vb

Imports Telerik.WebControls
Imports System.Data.OleDb
Imports System.Data
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.HtmlControls

Partial Class _TestFilter
    Inherits System.Web.UI.Page

    Dim ds As DataSet
    Dim dsTemp As DataSet
    Public Function SelectDistinct(ByVal TableName As String, ByVal SourceTable As DataTable, ByVal FieldName As String) As DataTable
         Dim dt As New DataTable(TableName)
        dt.Columns.Add(FieldName, SourceTable.Columns(FieldName).DataType)

         Dim LastValue As Object = Nothing

         Dim dr As DataRow
         For Each dr In SourceTable.Select("", FieldName)
             If LastValue Is Nothing Or Not ColumnEqual(LastValue, dr(FieldName)) Then
                LastValue = dr(FieldName)
                dt.Rows.Add( New Object() {LastValue})
             End If
         Next dr
         If Not (dsTemp Is Nothing) Then
            dsTemp.Tables.Add(dt)
         End If
         Return dt
    End Function 'SelectDistinct


    Private Function ColumnEqual(ByVal A As Object, ByVal B As Object) As Boolean

         ' Compares two values to see if they are equal. Also compares DBNULL.Value.
         ' Note: If your DataTable contains object fields, then you must extend this
         ' function to handle them in a meaningful way if you intend to group on them.
         If A Is DBNull.Value And B Is DBNull.Value Then ' both are DBNull.Value
             Return True
         End If
         If A Is DBNull.Value Or B Is DBNull.Value Then ' only one is DBNull.Value
             Return False
         End If
         Return A.Equals(B) ' value type standard comparison
    End Function 'ColumnEqual


    Private Function GetDropDownListItems(ByVal SourceDS As DataSet, ByVal ColumnName As String, ByVal pattern As String, ByVal filteredColumnName As String) As DataView
         Dim dsColumn As New DataSet(ColumnName)

        dsColumn.Tables.Add(ColumnName)

        dsColumn.Tables(0).Columns.Add(ColumnName, SourceDS.Tables(0).Columns(ColumnName).DataType)

         Dim row As DataRow
         For Each row In SourceDS.Tables(0).Rows
             If pattern Is Nothing Then
                dsColumn.Tables(0).Rows.Add( New Object() {row(ColumnName)})
             ElseIf CStr(row(filteredColumnName)) = pattern Then
                dsColumn.Tables(0).Rows.Add( New Object() {row(ColumnName)})
             End If
         Next row

         Dim dv As DataView = SelectDistinct(dsColumn.Tables(0).TableName, dsColumn.Tables(0), ColumnName).DefaultView
        dv.Sort = ColumnName + " ASC"

         Return dv
    End Function 'GetDropDownListItems


    Protected Sub RadGrid1_NeedDataSource(ByVal source As Object, ByVal e As Telerik.WebControls.GridNeedDataSourceEventArgs) Handles RadGrid1.NeedDataSource
        ds = DataSourceHelperVB.GetDataSet("SELECT ContactName, City, Country FROM Customers")
         Me.RadGrid1.MasterTableView.Columns.Clear()


         Dim dataColumn As DataColumn
         For Each dataColumn In ds.Tables(0).Columns
             Dim gridColumn As New RadGrid.MyCustomFilteringColumn()
             Me.RadGrid1.MasterTableView.Columns.Add(gridColumn)

            gridColumn.DataField = dataColumn.ColumnName
            gridColumn.HeaderText = dataColumn.ColumnName
            gridColumn.ListDataSource = GetDropDownListItems(ds, dataColumn.ColumnName, CStr(Session("SelectedValue")), CStr(Session("FilteredColumnName")))
         Next dataColumn

         Me.RadGrid1.DataSource = ds
    End Sub

    Protected Sub RadGrid1_ColumnCreating(ByVal sender As Object, ByVal e As Telerik.WebControls.GridColumnCreatingEventArgs) Handles RadGrid1.ColumnCreating
         If e.Column Is Nothing Then
            e.Column = New RadGrid.MyCustomFilteringColumn()
         End If
    End Sub


    Protected Sub clrFilters_Click(ByVal sender As Object, ByVal e As EventArgs)
         Dim column As GridColumn
         For Each column In RadGrid1.MasterTableView.Columns
            column.CurrentFilterFunction = GridKnownFunction.NoFilter
            column.CurrentFilterValue = [String].Empty
         Next column
        Session( "SelectedValue") = Nothing
        Session( "FilteredColumnName") = Nothing

        RadGrid1.MasterTableView.FilterExpression = [String].Empty
        RadGrid1.MasterTableView.Rebind()
    End Sub 'clrFilters_Click
End Class
VB.NET Copy Code
MyCustomFilteringColumn.vb

Imports Microsoft.VisualBasic
Imports System.Data
Imports Telerik.WebControls
Imports System.Web.UI.WebControls
Imports System.Web.UI
Imports System
Imports System.Collections

Namespace RadGrid

    Public Class MyCustomFilteringColumn
        Inherits GridBoundColumn

         Private lDataSource As Object = Nothing
         'RadGrid will call this method when it initializes the controls inside the filtering item cells

        Protected Overrides Sub SetupFilterControls(ByVal cell As TableCell)
            MyBase.SetupFilterControls(cell)
            cell.Controls.RemoveAt(0)

             Dim list As New System.Web.UI.WebControls.DropDownList()
            list.ID = "list" + Me.DataField

            list.Height = Unit.Pixel(300)
            list.AutoPostBack = True

            AddHandler list.SelectedIndexChanged, AddressOf list_SelectedIndexChanged

            cell.Controls.AddAt(0, list)
            cell.Controls.RemoveAt(1)

            list.DataTextField = Me.DataField
            list.DataValueField = Me.DataField

            list.DataSource = Me.listDataSource
            list.DataBind()
         End Sub 'SetupFilterControls

         Sub list_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
             Dim ddl As DropDownList = CType(sender, DropDownList)
             Dim filterItem As GridFilteringItem = CType(ddl.NamingContainer, GridFilteringItem)

             Me.SelectedValue = CType(sender, DropDownList).SelectedValue
             Me.FilteredColumnName = Me.UniqueName

             If Me.UniqueName = "Index" Then
                 'this is example for filtering for integer column type
                filterItem.FireCommandEvent( "Filter", New Pair("EqualTo", Me.UniqueName))
             End If
             'filtering for string column type
            filterItem.FireCommandEvent( "Filter", New Pair("Contains", Me.UniqueName))
         End Sub

         Public Property ListDataSource() As Object
             Get
                 Return Me.lDataSource
             End Get
             Set(ByVal value As Object)
                lDataSource = value
             End Set
         End Property


         Public Property SelectedValue() As String
             Get
                 Dim res As Object = Me.Owner.OwnerGrid.Page.Session( "SelectedValue")
                 If Not (res Is Nothing) Then
                     Return CStr(res)
                 End If
                 Return ""
             End Get
             Set(ByVal value As String)
                 Me.Owner.OwnerGrid.Page.Session( "SelectedValue") = value
             End Set
         End Property

         Public Property FilteredColumnName() As String
             Get
                 Dim res As Object = Me.Owner.OwnerGrid.Page.Session( "FilteredColumnName")
                 If Not (res Is Nothing) Then
                     Return CStr(res)
                 End If
                 Return ""
             End Get
             Set(ByVal value As String)
                 Me.Owner.OwnerGrid.Page.Session( "FilteredColumnName") = value
             End Set
         End Property

         'RadGrid will call this method when the value should be set to the filtering input control(s)
        Protected Overrides Sub SetCurrentFilterValueToControl(ByVal cell As TableCell)
            MyBase.SetCurrentFilterValueToControl(cell)

             Dim list As System.Web.UI.WebControls.DropDownList = CType(cell.Controls(0), System.Web.UI.WebControls.DropDownList)
             If Me.CurrentFilterValue <> String.Empty Then
                list.SelectedValue = Me.CurrentFilterValue
             End If
         End Sub 'SetCurrentFilterValueToControl

         'RadGrid will call this method when the filtering value should be extracted from the filtering input control(s)
        Protected Overrides Function GetCurrentFilterValueFromControl(ByVal cell As TableCell) As String
             Dim list As System.Web.UI.WebControls.DropDownList = CType(cell.Controls(0), System.Web.UI.WebControls.DropDownList)

             Return list.SelectedValue
         End Function 'GetCurrentFilterValueFromControl

         Public Overrides Function Clone() As GridColumn
             Return MyBase.Clone()
         End Function 'Clone

        Protected Overrides Function GetFilterDataField() As String
             Return Me.DataField
         End Function 'GetFilterDataField

    End Class

End Namespace