Sorting on fields bound to object properties

 Hi,

I have implemented a custom gridview control 'SortableGridView' which handles sorting and paging of the gridview when bound to custom types (objects).

However, I am not able to figure out how to get the sorting working for columns that are bound to object properties.

For example, if my SortableGridView is bound to a collection of Car objects, and within the _RowDataBound event, I bind a column to the Colour of the Dashboard of the Car like this:

 

    protected void SortableGridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Cells[5].Text = ((Car)(e.Row.DataItem)).Dashboard.Colour.ToString();
}
}
  

And then I want the column to be sortable so within the .aspx file i set the SortExpression like this:

 

<asp:BoundField HeaderText="Dashboard Colour" SortExpression="Dashboard.Colour"/>

The problem is that i get the following runtime error:

SortExpression is invalid. Object Car has no public Property called dashboard.Colour

The Car object does indeed have a public property which returns a Dashboard object. In turn, the Dashboard object does indeed have a public property Colour which returns a String object.

So, does anyone know how i can get the sorting working???

Also, does anyone know if the normal gridview is able to sort on 'nested' public object properties eg ObjectProperty.Object2Property. If so, then perhaps the problem lies within my implementation of the SortableGridView and its sorting procedures...???

0
lukejenner
3/25/2008 11:41:47 AM
asp.net.presentation-controls 72751 articles. 3 followers. Follow

4 Replies
232 Views

Similar Articles

[PageSpeed] 48

Hi lukejenner ,

You have to write some code in Sorting event of gridview to do this.

Besides , you have to implemenet the function to sort custom object.

I wrote one sample for you. The Sorting in the sample is used to do custom object sorting.

 

    public class Sorting
    {

        public enum SortDirection
        {
            Ascending,
            Descending
        }

        private class DTOComparer : IComparer
        {

            private ObjectComparer comparer;

            public DTOComparer(string propertyName)
            {
                comparer = new ObjectComparer(propertyName, SortDirection.Ascending);
            }

            public DTOComparer(string propertyName, SortDirection Direction)
            {
                comparer = new ObjectComparer(propertyName, Direction);
            }

             int IComparer.Compare(T x, T y)
            {
                return comparer.docompare(x, y);
            }




            
        }

        public class ObjectComparer : IComparer
        {

            private string _prop = "";
            private SortDirection _dir;

            public int docompare(object x, object y)
            {
                return ((IComparer)this).Compare(x,y);
            }

            public ObjectComparer(string propertyName)
            {
                _prop = propertyName;
                _dir = SortDirection.Ascending;
            }

            public ObjectComparer(string propertyName, SortDirection Direction)
            {
                _prop = propertyName;
                _dir = Direction;
            }

              int System.Collections.IComparer.Compare(object x, object y)
            {

                if (!(x.GetType().ToString() == y.GetType().ToString()))
                {
                    throw new ArgumentException("Objects must be of the same type");
                }

                PropertyInfo propertyX = x.GetType().GetProperty(_prop);
                PropertyInfo propertyY = y.GetType().GetProperty(_prop);

                object px = propertyX.GetValue(x, null);
                object py = propertyY.GetValue(y, null);

                if (px == null && py == null)
                {
                    return 0;
                }
                else if (px != null && py == null)
                {
                    if (_dir == SortDirection.Ascending)
                    {
                        return 1;
                    }
                    else
                    {
                        return -1;
                    }
                }
                else if (px == null && py != null)
                {
                    if (_dir == SortDirection.Ascending)
                    {
                        return -1;
                    }
                    else
                    {
                        return 1;
                    }
                }
                else if (px.GetType().GetInterface("IComparable") != null)
                {
                    if (_dir == SortDirection.Ascending)
                    {
                        return ((IComparable)px).CompareTo(py);
                    }
                    else
                    {
                        return ((IComparable)py).CompareTo(px);
                    }
                }
                else
                {
                    return 0;
                }

            }

        }

        /// <summary> 
        /// Sorts the list of objects based on the property and direction given. 
        /// </summary> 
        /// <typeparam name="T">Object Type to be sorted</typeparam> 
        /// <param name="list">Generic List of objects of the same type</param> 
        /// <param name="propertyName">A property name of the object</param> 
        /// <param name="direction">Ascending or Descending from System.Web.UI.WebControls.SortDirection enumeration.</param> 
        /// <returns>A Generic List of objects sorted by the property and direction given.</returns> 
        /// <remarks>Throws a null reference exception if the property does not exist.</remarks> 
        public List SortList(List list, string propertyName, SortDirection direction)
        {

            DTOComparer comparer = new DTOComparer(propertyName, direction);
            list.Sort(comparer);
            return list;

        }

        public DataView SortList(DataView dv, string propertyName, SortDirection direction)
        {
            string dir = string.Empty;
            if (direction == SortDirection.Descending)
            {
                dir = "DESC";
            }
            else
            {
                dir = "ASC";
            }
            dv.Sort = propertyName + " " + dir;
            return dv;
        }

        public DataTable SortList(DataTable dt, string propertyName, SortDirection direction)
        {
            return SortList(dt.DefaultView, propertyName, direction).ToTable();
        }

        public Array SortList(Array arr, string propertyName, SortDirection direction)
        {

            ObjectComparer comparer = new ObjectComparer(propertyName, direction);
            Array.Sort(arr, comparer);
            return arr;

        }

    } 




    public class Person
    {
        string name;

        int id;

        public int Id
        {
            get { return id; }
            set { id = value; }
        }



        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public Person(string name)
        {
            this.name = name;
        }

        public string GetName()
        {
            return this.name;
        }


        
    }

 Code in page:

<asp:GridView ID="GridView1" runat="server" AllowSorting="True" 
        onsorting="GridView1_Sorting">
    </asp:GridView>

 code behind:

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                bind();
        }


        private List Getdata()
        {
            Person p1 = new Person("bbb");
            Person p2 = new Person("aaa");

            List lst = new System.Collections.Generic.List();

            lst.Add(p1);
            lst.Add(p2);

            return lst;

        }

        private void bind()
        {
            List lst = Getdata();
            this.GridView1.DataSource = lst;
            GridView1.DataBind();
        }

        protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
        {
            string sortExpression = e.SortExpression;
            ViewState["z_sortexpresion"] = e.SortExpression;
            if (GridViewSortDirection == SortDirection.Ascending)
            {
                GridViewSortDirection = SortDirection.Descending;
                SortGridView(sortExpression, "DESC");
            }
            else
            {
                GridViewSortDirection = SortDirection.Ascending;
                SortGridView(sortExpression, "ASC");
            }
        }

        public SortDirection GridViewSortDirection
        {
            get
            {
                if (ViewState["sortDirection"] == null)
                    ViewState["sortDirection"] = SortDirection.Ascending;
                return (SortDirection)ViewState["sortDirection"];
            }
            set
            {
                ViewState["sortDirection"] = value;
            }
        }



        private void SortGridView(string sortExpression, string direction)
        {
            List source = Getdata();
            IList res = null;
            if (direction == "ASC")
            {
                Sorting sort = new Sorting();
                res = sort.SortList(source, sortExpression, Sorting.SortDirection.Ascending);

       
            }
            else
            {
                Sorting sort = new Sorting();
                res = sort.SortList(source, sortExpression, Sorting.SortDirection.Descending);

            }


            this.GridView1.DataSource = res;
            GridView1.DataBind();
           
            


            //Sorting sort = new Sorting();

            //IList<Person> res = sort.SortList<Person>(lst, "Name", Sorting.SortDirection.Ascending);

       
        }

 

 



Samu Zhang
Microsoft Online Community Support

Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question.
0
Samu
3/26/2008 10:06:30 AM

Hi ...

Thank you for your incredibly detailed response!

I dont think i explained my problem specifically enough. 

I have already written the custom sorting functionality, quite similar to yours.

I will rephrase the problem in the context of your code.

If the Person object also had an extra public property Hair which returned a Hair object. The Hair object has a public property Color. In the code behind file you can bind a column of the GridView to a Person's Hair.Color within the RowDataBound event as such:

 

e.Row.Cells[6].Text = ((Person)(e.Row.DataItem)).Hair.Color;
  

As i see it, the problem lies in the following code:

 

1    PropertyInfo propertyX = x.GetType().GetProperty(_prop);
2    PropertyInfo propertyY = y.GetType().GetProperty(_prop);
3    
4    object px = propertyX.GetValue(x, null);
5    object py = propertyY.GetValue(y, null);

I believe line 4 would throw a null object exception.

I have realised that the error mentinoed in my first post was a custom exception that my code throws when, in the context of your example, px is null.

So, basically, what i need is to write some code that will recursively traverse a tree of parent-child object relationships until it reaches the final property that it is to be compared.

Unless anyone else can think of another way to achieve the desired sorting functionality?
 

0
lukejenner
3/27/2008 10:03:05 AM

Hi lukejenner ,

Thank you take time to read my code so carefully.

In this scenario, you want to sort the object by the nested object's property. This thing become a little complex.

The simplest way I can find is to use Linq.

see my sample,

how to use :

 

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                bind();
        }


        private void bind()
        {
            List lst = Getdata();
            var sels = from p in lst
                       orderby p.Hair.Color ascending
                       select p;
            this.GridView1.DataSource = sels;
            GridView1.DataBind();
        }



        private List Getdata()
        {
            PersonWithHair p1 = new PersonWithHair(1,"samu","bbbb");
            PersonWithHair p2 = new PersonWithHair(2,"liu","aaaa");

            List lst = new System.Collections.Generic.List();

            lst.Add(p1);
            lst.Add(p2);

            return lst;

        }

 

Some Class:

   public class Hair
    {
        private string color;

        public string Color
        {
            get { return color; }
            set { color = value; }
        }

    }


    public class PersonWithHair
    {
        Hair hair;

        public Hair Hair
        {
          get { return hair; }
          set { hair = value; }
        }

        string name;

        int id;

        public int Id
        {
            get { return id; }
            set { id = value; }
        }



        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public PersonWithHair(int id,string name,string color)
        {
            this.id = id;
            this.name = name;
            this.hair = new Hair();
            hair.Color = color;
        }

        public string GetName()
        {
            return this.name;
        }



    }

 

 



Samu Zhang
Microsoft Online Community Support

Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question.
0
Samu
3/28/2008 3:08:27 AM

 Hi Samu,

Again, thank you for the detailed resposne. I've never used LINQ before but that looks great!

Im using VS2005 so will have to do a little extra work to get LINQ integrated into my environment and dont have time to do it today...

I'll have a look into as soon as i have a chance.

Thanks again for the suggestion!

Luke.
 

0
lukejenner
3/28/2008 9:58:19 AM
Reply: