{"id":150,"date":"2011-01-14T13:36:11","date_gmt":"2011-01-14T12:36:11","guid":{"rendered":"http:\/\/thoughtflow.dk\/?p=150"},"modified":"2011-01-14T13:36:11","modified_gmt":"2011-01-14T12:36:11","slug":"sorting-asp-net-gridview-with-custom-data-source","status":"publish","type":"post","link":"https:\/\/davidlebech.com\/thoughtflow\/sorting-asp-net-gridview-with-custom-data-source\/","title":{"rendered":"Sorting an ASP.NET GridView with a custom data source"},"content":{"rendered":"<p>The other day, I had to present some data in an ASP.NET <code>GridView<\/code> control and I had to allow sorting of some of the columns. This is easy if the data source is based on e.g. an SQL data source but not particularly easy when using a custom data source. In my case, I had a simple array of custom objects containing invoice data.<\/p>\n<p>There are many solutions for this problem on the web but none of them really seemed to do the trick in my case. Many of the solutions I could find rely on converting the data source into a <code>DataTable<\/code> object but this is not a good idea when sorting needs to be applied to many fields because the <code>DataTable<\/code> needs to have these fields added manually.<\/p>\n<p>So without further ado, here is a simple solution for the problem. First, let&#8217;s say our custom data source consists of an array of <code>Person<\/code> objects, defined like this:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nclass Person {\r\n  string Name { get; set; }\r\n  DateTime BirthDay { get; set; }\r\n}\r\n<\/pre>\n<p>The <code>GridView<\/code> to bind the persons to is very simple:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&lt;asp:GridView ID=&quot;PersonGridView&quot; runat=&quot;server&quot;\r\n  AutoGenerateColumns=&quot;false&quot;\r\n  AllowSorting=&quot;true&quot;\r\n  OnSorting=&quot;personGridView_Sorting&quot;&gt;\r\n  &lt;Columns&gt;\r\n    &lt;asp:BoundField DataField=&quot;Name&quot; SortExpression=&quot;Name&quot; \/&gt;\r\n    &lt;asp:BoundField DataField=&quot;BirthDay&quot; SortExpression=&quot;BirthDay&quot; \/&gt;\r\n  &lt;\/Columns&gt;\r\n&lt;\/asp:GridView&gt;\r\n<\/pre>\n<p>The SortExpression has to be exactly the same name as the field. The sort direction and sort expression are stored in the <code>ViewState<\/code> and accessed by two fields in code-behind:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nprivate SortDirection CurrentSortDirection {\r\n  get {\r\n    if (ViewState&#x5B;&quot;SortDirection&quot;] == null)\r\n      ViewState&#x5B;&quot;SortDirection&quot;] = SortDirection.Ascending;\r\n    return (SortDirection)ViewState&#x5B;&quot;SortDirection&quot;];\r\n  }\r\n  set { ViewState&#x5B;&quot;SortDirection&quot;] = value; }\r\n}\r\n\r\nprivate string CurrentSortExpression {\r\n  get {\r\n    if (ViewState&#x5B;&quot;SortExpression&quot;] == null)\r\n      ViewState&#x5B;&quot;SortExpression&quot;] = &quot;Name&quot;;\r\n    return (string)ViewState&#x5B;&quot;SortExpression&quot;];\r\n  }\r\n  set { ViewState&#x5B;&quot;SortExpression&quot;] = value; }\r\n}\r\n<\/pre>\n<p>Notice that I have chosen to have <code>Name<\/code> sorted in ascending order by default. This is of course not necessary but convenient for later. Next up is the <code>OnSorting<\/code> event:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nprotected void personGridView_Sorting(object sender, GridViewSortEventArgs e) {\r\n  CurrentSortExpression = e.SortExpression;\r\n\r\n  \/\/ Ignore e.SortDirection since it is always Ascending, an apparent bug in .NET\r\n  if (CurrentSortDirection == SortDirection.Ascending)\r\n    CurrentSortDirection = SortDirection.Descending;\r\n  else\r\n    CurrentSortDirection = SortDirection.Ascending;\r\n\r\n  BindPersonData();\r\n}\r\n<\/pre>\n<p>Finally, we can write our binding method.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nprivate void BindPersonData() {\r\n  Person&#x5B;] persons = GetPersonArrayFromSomewhere();\r\n\r\n  \/\/ Sort the persons, if sorting is applied.\r\n  if (!string.IsNullOrEmpty(CurrentSortExpression)) {\r\n    System.Reflection.PropertyInfo personInfo = (typeof(Person)).GetProperty(CurrentSortExpression);\r\n    if (CurrentSortDirection == SortDirection.Ascending) {\r\n      persons = persons\r\n        .OrderBy(person =&gt; personInfo.GetValue(person, null))\r\n        .ToArray();\r\n    }\r\n    else {\r\n      persons = persons\r\n        .OrderByDescending(person =&gt; personInfo.GetValue(person, null))\r\n        .ToArray();\r\n    }\r\n  }\r\n\r\n  PersonGridView.DataSource = persons;\r\n  PersonGridView.DataBind();\r\n}\r\n<\/pre>\n<p>The sorting can also be applied with a different sorting method. I happen to like Linq so that is why I have used it here. The key thing to notice here is how the correct field for sorting is found with the use of the reflection framework:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nSystem.Reflection.PropertyInfo personInfo = (typeof(Person)).GetProperty(CurrentSortExpression);\r\n<\/pre>\n<p>That is why it is essential that the SortExpression value in the <code>GridView<\/code> is the same as the field&#8217;s name.<\/p>\n<p>Oh and by the way, paging with a custom data source is very easy. Add this to your <code>GridView<\/code>:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nAllowPaging=&quot;true&quot;\r\nOnPageIndexChanging=&quot;personGridView_PageIndexChanging&quot;\r\n<\/pre>\n<p>and this method to the code-behind:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nprotected void personGridView_PageIndexChanging(object sender, GridViewPageEventArgs e) {\r\n  PersonGridView.PageIndex = e.NewPageIndex;\r\n  BindPersonData();\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The other day, I had to present some data in an ASP.NET GridView control and I had to allow sorting of some of the columns. This is easy if the data source is based on e.g. an SQL data source but not particularly easy when using a custom data source. In my case, I had [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29],"tags":[46,51,52,32],"class_list":["post-150","post","type-post","status-publish","format-standard","hentry","category-tips","tag-net","tag-gridview","tag-sorting","tag-tip"],"_links":{"self":[{"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/posts\/150","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/comments?post=150"}],"version-history":[{"count":0,"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/posts\/150\/revisions"}],"wp:attachment":[{"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/media?parent=150"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/categories?post=150"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/davidlebech.com\/thoughtflow\/wp-json\/wp\/v2\/tags?post=150"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}