Saturday, October 22, 2011

Sorting Data with the ListView Control


Introduction


When binding data to a GridView using a data source control, enabling sorting is as simple as ticking the "Enable Sorting" checkbox in the GridView's Smart Tag. Enabling sorting turns each GridView column's header into a LinkButton that, when clicked, causes a postback and re-binds the data to the GridView, sorting it by the clicked column.
Likewise, enabling sorting in the ListView is a fairly simple and straightforward process and can be accomplished without writing a line of code. The main challenge with enabling sorting in the ListView control is that there are no pre-defined columns, like with the GridView. Therefore, we are on the hook for defining and implementing the sorting interface. But once this is setup, the ListView can internally handle the sorting logic without the need for us to write any additional code. Of course, in more advanced scenarios we may need to manually sort the data or programmatically specify the sort expression used to internally sort the data. Prior to sorting, the ListView raises itsSorting event handler, which is an ideal place to add any additional sort-related logic. Moreover, the ListView control's sorting logic can be programmatically invoked via itsSort method.
In this article we will look at how to enabling sorting in the ListView control. We will look at using the ListView's simple, baked in sorting functionality. Following that, we will see how to programmatically invoke this sorting logic via the Sort method as well as more advanced scenarios. Read on to learn more!

Sorting Basics


The core concepts used to sort the ListView are the same as those used to sort the other *View Web controls: the GridView, DetailsView, and FormView. For starters, the ListView control has a Sort(sortExpressionsortDirection) method that takes as input a string sortExpression and a SortDirection enumeration (which has values ofAscending and Descending). Calling the Sort method raises the ListView control's Sorting event.If the ListView control is bound to a data source control (like a SqlDataSource or ObjectDataSource control), the data source control is responsible for sorting the data. This sorted data is then automatically re-bound to the ListView control. In short, if you are using a data source control then sorting can be implemented in the ListView control without having to write a single line of code - it is all handled automatically by the ListView and its data source control.
If the ListView is programmatically bound to data - that is, if you have code that retrieves the data, then assigns it to the ListView's DataSource property, and then calls the ListView's DataBind() method - then you are responsible for re-sorting the data and re-binding it to the ListView. To accomplish this, you will need to create an event handler for the ListView's Sorting event, which is where you would requery the data in the specified sorted order and re-bind it to the ListView. We will look at creating aSorting event handler later in this article (although we will use it for more advanced sorting features; all of the demos here use a data source control).
So one way to kick off the ListView's sorting workflow is to call the Sort method. Another way is to add a properly configured LinkButton, Button, or ImageButton to the ListView's LayoutTemplate. Such a Button, when clicked, will cause a postback and initiate the sorting workflow. For this to work, the LinkButton, Button, or ImageButton must have its CommandName property set to Sort and its CommandArgument property set to the data field to sort by.

Implementing a Simple, Code-Free Sorting Example


To illustrate using the ListView's built-in sorting mechanism, let's look at a simple example. (This example, as well the others examined here, are available for download at the end of this article.) Let's extend the Product Listing example from the Displaying Data with the ListView article to include a sorting interface. In particular, let's add two sorting options: to sort by ProductName and to sort by UnitPrice. To accomplish this, simply add two LinkButtons (or Buttons or ImageButtons) in the ListView's LayoutTemplate with appropriate CommandName and CommandArgument property values.
<asp:ListView ID="ProductList" runat="server" DataSourceID="ProductDataSource">
   <LayoutTemplate>
      <h3>Product Listing</h3>
      [<asp:LinkButton runat="server" ID="SortByName" CommandName="Sort"
                     CommandArgument="ProductName">Sort by Name</asp:LinkButton>]
      | [<asp:LinkButton runat="server" ID="SortByPrice" CommandName="Sort"
                     CommandArgument="UnitPrice">Sort by Price</asp:LinkButton>]

      <blockquote>
         <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder>
      </blockquote>
   </LayoutTemplate>

   ...
</asp:ListView>

(I've left out much of the ListView's markup for brevity, focusing instead on the two sorting LinkButtons. I also omitted the AccessDataSource control that is bound to this ListView.)
Note that the two sorting LinkButtons have their CommandName properties set to Sort and their CommandArgument properties set to the appropriate data field names. When one of these LinkButtons are clicked, a postback will occur and the ListView will automatically call its Sort method passing in the LinkButton's CommandArgument value as the sort expression. The sort direction is maintained in an internal variable and automatically toggles if the same sort expression is sorted twice in a row. Consequently, if a visitor clicks the "Sort by Price" link twice in a row, the first time the products will be sorted in ascending order (from cheapest to most expensive), but the second time the sort order will reverse.
The following two screen shots show the sorting LinkButtons in action. The first screen shot shows the screen after the "Sort by Price" link has been clicked for the first time. The products are ordered in ascending order.

The ListView, sorted by UnitPrice in ascending order.
The second screen shot shows the results after the "Sort by Price" link has been clicked again; this time the products are ordered in descending order.

The ListView, sorted by UnitPrice in descending order.

Sorting via the Sort Method


The previous example looked at how to have the ListView's data sorted in response to a user clicking a LinkButton, Button, or ImageButton control. But in certain scenarios we may need to programmatically invoke the ListView's sorting workflow. For example, when a page is first loaded we may want to sort the data, or when some other user interface element on the page is engaged in some manner, we may want to resort the results. This can be accomplished by calling the ListView control's Sort method, passing in the sort expression and direction.To illustrate using this method, let's add a Button Web control to the above example that, when clicked, will sort the results by the CategoryName field. Simply add a Button Web control, set its properties, and then create a Click event handler. In this event handler simply call the ListView's Sort method passing in "CategoryName" as the sort expression and SortDirection.Ascending as the sort direction. That's all there is to it!

Protected Sub SortByCallingSortMethodButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SortByCallingSortMethodButton.Click
   'Sort the ListView by the CategoryName field in ascending order
   ProductList.Sort("CategoryName", SortDirection.Ascending)End Sub

Clicking the Button causes a postback and calls the Sort method, which invokes the sorting workflow. It's just as if we had added a LinkButton to the LayoutTemplate and set its CommandName and CommandArgument properties to Sort and "CategoryName".

The ListView, sorted by CategoryName in ascending order.

Creating an Event Handler for the ListView's Sorting Event


In certain scenarios we may need to execute code during the ListView's sorting workflow. This can be accomplished by creating an event handler for the ListView's Sortingevent, which is raised at the start of the sorting workflow. If the ListView's data has been programmatically bound then it is imperative that we create a Sorting event handler that re-sorts the data and re-binds it to the ListView. But even when we are using declarative data source controls, we may need to step into the sorting workflow and perform some custom logic.The download available at the end of this article includes an example that "remembers" the user's five most recent sorting options and displays them on the page as a series of LinkButtons. Clicking one of these LinkButtons re-applies the specified sort order. In creating this feature I needed to create an event handler for the Sorting event so that I could record the just-applied sort expression and direction.
Each of the user's past sort choices are cataloged by a SortHistory object, which is a class I created in the website's App_Code folder. This class has SortExpression andSortDirection properties as well as read-only properties that return the SortExpression and SortDirection properties as a single string and as a formatted string. The formatted string is used for display purposes. It converts SortExpression and SortDirection values of "CategoryName" and SortDirection.Descending, for example, to a more human-friendly output, like: "Category (in descending order)".
I also created a SortHistoryQueue object in the App_Code folder, which holds a buffer of at most five SortHistory objects. A SortHistoryQueue instance is stored in Session so that the user's sort choices are remembered across postbacks, across page visits, and are specific to a user.
In addition to using the Sorting event handler, this demo also uses the Sort method. As I noted earlier, the user's five most recent sort choices are displayed as a bulleted list of LinkButtons; this is rendered using a Repeater control. Clicking a LinkButton causes a postback and raises the Repeater's ItemCommand event. I created an event handler for this event that applies the sort criteria used for the clicked link by calling the ListView's Sort method.
The following screen shot shows this functionality in action. The first screen shot shows the output with the five most recent sort options listed.

The five most recent sort choices are listed as LinkButtons.
The second screen shot is taken right after the "Sort by Name" LinkButton has been clicked. Note the addition of the "Name (in ascending order)" LinkButton to the bulleted list (as well as the fact that the results are sorted by ProductName in ascending order).

The results have been sorted by ProductName, in ascending order.

Conclusion


In this article we looked at how to sort the data in a ListView control. Like with the GridView, DetailsView, and FormView controls, a ListView's data can be sorted without having to write a lick of code. As we saw in our first example, all that we need to do is create the sorting interface: Buttons, LinkButtons, or ImageButtons with appropriateCommandName and CommandArgument settings. Such button controls, when clicked, cause a postback and trigger the ListView's sorting workflow. The sorting workflow can also be invoked by explicitly calling the Sort method, passing in the sort expression and direction.While code-free sorting is definitely possible, there may be circumstances when you need to manually sort the data or perform additional sort-related logic. In such cases, simply create an event handler for the ListView's Sorting event (which fires at the start of the sorting workflow). The download available at the end of this article includes an example that uses the Sorting event handler to remember the current user's five most recent sort choices.