Skip to main content Skip to footer

Implementing Filtering & Sorting on C1TrueDbGrid bound to Business Objects

More often than not, with C1TrueDbGrid bound to a business object, users face issues with filter and sort. Ideally, sort is implemented on a field in underlying DataSource (if bound to a dataSet/DataView, ). This happens whenever user clicks on any Column header bound to that field. And the sort happens by comparing values of same datatype in the underlying field. Well, the same doesn't happen with a List. Since a Generic List does not specify a data type, that is, the data type of the items in the List is set with a type parameter, how would one write a Find() function for a Generic List? Since nothing is known about the data type being searched for, one cannot know what sort of condition is used to discriminate between one item and another. Lets create a class and see how to go about it. Say, we've the following class:


    public class MyPerson  
    {  
        public int ID { get; set; }  
        public string Name { get; set; }  
        public MyPerson(int id, string name)  
        {  
            ID = id;  
            Name = name;  
        }  
    }  

Now, create a list of the class and fill it with some data:


            List<MyPerson> people = new List<MyPerson>();  
            people.Add(new MyPerson(1, "Ron"));  
            people.Add(new MyPerson(2, "Liv"));  
            people.Add(new MyPerson(3, "Greg"));  
            people.Add(new MyPerson(4, "Steve"));  
            people.Add(new MyPerson(5, "John"));  
            people.Add(new MyPerson(6, "Dan"));  
            people.Add(new MyPerson(7, "Frederick"));  
            people.Add(new MyPerson(8, "Tom"));  
            people.Add(new MyPerson(9, "Robert"));  
            people.Add(new MyPerson(10, "Patrick"));  

Now, bind the grid to this object. And also, set AllowSort and AllowFilter to False because we're going to handle both ourselves.


            //Bind the grid to the list object.  
            c1TrueDBGrid1.SetDataBinding(people, "");  
            //We'd handle sort &amp; Filter ourselves  
            c1TrueDBGrid1.AllowSort = false;  
            c1TrueDBGrid1.AllowFilter = false;  

Oh yes, we'd need ascending and descending sort glyphs too.


 // get the sorting indicators that we'll use in the column headers  
 _sortdn  = new Bitmap("..\\\..\\\SortDn.bmp");  
 _sortup = new Bitmap("..\\\..\\\SortUp.bmp");  
 _sortdn.MakeTransparent(Color.Red);  
 _sortup.MakeTransparent(Color.Red);  

This is how raw grid looks like. Now usually, to sort a custom class, you'd have to have your class inherit from the IComparable interface, and implement your own CompareTo method. Now, there's a nice little technique to easily sort a generic List using delegates. Using anonymous delegates, you can use the Sort method any way you want with just a single line of code. To implement Sort on a field in a grid, bound to a List object, we need to listen to the HeadClick event. Thereafter, following code sorts the list by any field that's clicked on, using anonymous delegates: Here's the code:


 try  
 {  
  if (e.Column.Name == "ID")  
  {  
   if (!col2_Asc)  
   {  
    people.Sort(delegate(MyPerson mp1, MyPerson mp2) { return mp1.ID.CompareTo(mp2.ID); });  
    col2_Asc = true;  
    this.c1TrueDBGrid1.Splits[0].DisplayColumns["ID"].HeadingStyle.ForegroundImage = this._sortup;  
   }  
  else if (col2_Asc)  
  {  
   people.Sort(delegate(MyPerson mp1, MyPerson mp2) { return mp2.ID.CompareTo(mp1.ID); });  
   col2_Asc = false;  
   this.c1TrueDBGrid1.Splits[0].DisplayColumns["ID"].HeadingStyle.ForegroundImage = this._sortdn;  
 }  
  //Remove Sort Glyph from other column.  
  this.c1TrueDBGrid1.Splits[0].DisplayColumns["Name"].HeadingStyle.ForegroundImage = null;  
}  

  if (e.Column.Name == "Name")  
  {  
   if (!col1_Asc)  
   {  
    people.Sort(delegate(MyPerson mp1, MyPerson mp2) { return mp1.Name.CompareTo(mp2.Name); });  
    col1_Asc = true;  
    this.c1TrueDBGrid1.Splits[0].DisplayColumns["Name"].HeadingStyle.ForegroundImage = this._sortup;  
   }  
   else if (col1_Asc)  
   {  
    people.Sort(delegate(MyPerson mp1, MyPerson mp2) { return mp2.Name.CompareTo(mp1.Name); });  
    col1_Asc = false;  
    this.c1TrueDBGrid1.Splits[0].DisplayColumns["Name"].HeadingStyle.ForegroundImage = this._sortdn;  
   }  
  //Remove Sort Glyph from other column.  
  this.c1TrueDBGrid1.Splits[0].DisplayColumns["ID"].HeadingStyle.ForegroundImage = null;  
 }  

  //Set SortGlyph's position in Column Header  
  foreach (C1.Win .C1TrueDBGrid .C1DisplayColumn dc in this.c1TrueDBGrid1 .Splits [0].DisplayColumns )  
  dc.HeadingStyle.ForeGroundPicturePosition = C1.Win.C1TrueDBGrid.ForeGroundPicturePositionEnum.RightOfText;  
 }  
 catch (Exception ex)  
 {  
  MessageBox.Show(ex.Message);  
 }  
 finally  
 {  
  c1TrueDBGrid1.Rebind(true);  
 }  

You may've observed the code used to implement SortGlyphs in between. This is because, since we're handling filtering ourselves, we need to handle sortglyphs too. Here's the grid once sorted, say on 'Name' field: Similarly, you may filter the list by using the FindAll() method. The following code filters the list, at a ButtonClick event, as per user input in each of the two textboxes placed on the Form:


 string id = this.txtID .Text ;  
 string name = this.txtName.Text;  
 //Create a new list object; to be populated with filtered values.  
 List<MyPerson> p = new List<MyPerson>();  
 try  
  {  
   if (id != "" && name != "")  
   {  
    MyPerson _p = new MyPerson(int.Parse(id), name);  
    p = people.FindAll(delegate(MyPerson mp) { return (mp.ID == \_p.ID && mp.Name == \_p.Name); });  
   }  
   else if (id != "" && name == "")  
   {  
    p = people.FindAll(delegate(MyPerson mp) { return (mp.ID == int.Parse(id)); });  
   }  
   else if (id == "" && name != "")  
   {  
    p = people.FindAll(delegate(MyPerson mp) { return (mp.Name == name); });  
   }  
  }  
  catch (Exception ex)  
  {  
   MessageBox.Show(ex.Message);  
  }  
  finally  
   {  
    //bind the grid to the actual list object if both textboxes are blank.  
    if (id == "" && name == "")  
    {  
     c1TrueDBGrid1.SetDataBinding(people, "", true);  
    }  
    //else bind the grid to the new list object containing filtered values.  
   else  
   {  
    c1TrueDBGrid1.SetDataBinding(p, "", true);  
   }  
 }  

As shown above, we create a new list of the existing class, and populate it as per the filtered values and then bind the grid to the same, if both textboxes are 'not' blank. Else, we rebind the grid to the original List object. Here's a screenshot of the filtered grid: Download Sample

MESCIUS inc.

comments powered by Disqus