Introduction
I had the need to use a ListBox
control in a Silverlight 2 project to display many items (there were about 1500 players' names), and I faced some big performance problems: when binding the ListBox
(just setting its ItemSource
property), it takes more and more seconds to render the final control with the items loaded. Something not acceptable from a user experience perspective. Okay, everybody could ask "how useful is to show so many elements to the user"? In fact, also in my opinion, a user interface designed to show such big lists of items without any filtering facility for the user is the result of a poor design. But, I actually had this scenario: provide the users an alphabetical list of all the 1500 players, letting them to search a player by using a filter or eventually by scrolling the list alphabetically.
I immediately realized that a solution could be to provide the user with a "simulated" complete list: loading just the first items, letting them think it was the complete list, then intercepting the scrolling action to actually load more items. In other words: something similar to the lazy loading of records we see in the GUI of products like MS Access or SQL Server. But, even after loading items "on demand", the list became more and more heavy while scrolling, due to the increased number of loaded items, and the performance problems arose again. Then, I decided to abandon the "progressive loading" approach in favor of a "paged loading" approach combined with a search facility. The following paragraphs describe how I implemented this simple idea.
How the "paged searchable ListBox control" works
The attached sample code contains three experimental ListBox
es, used to show a big number of items, in-memory generated (and hosted by the AllItems
list).
In all the three examples, I'm using the classic, standard Silverlight 2 ListBox
control, but in different ways. And in all the three examples, the search facility is provided by the following simple function that uses a LINQ expression to extract from the complete list only the items containing a given string:
Private Function FilterItems(ByVal FilterTerm As String) As IEnumerable(Of String)
Dim result As IEnumerable(Of String)
If FilterTerm = "" Then
result = AllItems
Else
result = From s In AllItems Where
s.ToString().ToLower().Contains(FilterTerm.ToLower()) Select s
End If
Return result
End Function
In the first ListBox
, a simple binding is performed:
lstList1.ItemsSource = AllItems
As you can experiment by yourself on Silverlight 2, for 1500 elements or more, the loading performance is unacceptable. By using the search facility, the user can narrow down the number of items loaded in the ListBox
, but while typing the very first characters, the slowness remains unacceptable (until the filtered list is small enough to render quickly).
In the second and third ListBox
es, I'm using a "paged" approach. This means that the ListBox
contains a limited number of items, and the user is presented with some visual clue to navigate through the pages. The only difference between the second and the third example is in the visual clue presented to the user in order to navigate the pages: in example 2, it's immediately clear that the list is paged; in example 3, the clue is integrated as an additional list element the user discovers only by scrolling down the ListBox
. By keeping track of the number of the page currently displayed and the page dimension, the binding expression in these cases becomes something like:
lstList.ItemsSource = FilteredItems.Skip(PageNumber * PageDimension).Take(PageDimension)
With PageDimension
equals to, let's say, fifty, the ListBox
will never contain more than 50 items. Any rendering slowness is disappeared, and the user-friendliness is quite completely preserved.
Conclusions
The "paged searchable approach" described here is very simple, but it became absolutely a need for me to workaround the loading and rendering slowness of the Silverlight 2 ListBox
control. The good news is that the ListBox
control on Silverlight 3 seems to be free from these performance issues, because it now supports UI virtualization.