XAML Playground
about XAML and other Amenities

Metro: Incrementally load GridView and ListView with ISupportIncrementalLoading

2012-06-10T21:58:36+01:00 by codeblock

Developers that usually deal with web applications know that one of the pillar of this kind of applications is the use of paged result sets because moving a huge number of records form the server to the browser is not a good idea. Metro applications suffer the same problem. No matter that metro applications are not strictly web applications, the application architecture imply that the connection to a datasource have to be wrapped by web service call so the need of limiting the usage of the network is a strong requirements.

Metro introduces a new interesting method to mange pagine of data. Since the use of a common paging is deprecated by guidelines the requirement is to automatically load records when a user is about to the end of the items available on the user interface. This may be an hard task to do with components like GridView and ListView but thanks to the ISupportIncrementalLoading interface it may be easy like a game.

The ISupportIncrementalLoading interface has to be implemented by a collection. When a GridView detects this interface in the class provided in the ItemsSource property, it automatically change its behavior and works loading items only when they really need to fill empty space because the user reached the end. The definition of the interface is pretty simple:

   1: public interface ISupportIncrementalLoading
   2: {
   3:     bool HasMoreItems { get; }
   4:     IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count);
   5: }

The HasMoreItems property simply inform the consumer when there is more items to load. But the core of the interface is the LoadMoreItemsAsync method. As the name suggests this method works asynchronously and it have to load a number of items. The count parameter represents the number of items the consumer needs but the body of the method can load a different number, based on its paging size, and have to return the this number as a result into the LoadMoreItemsResult class.

You may expect that the loaded items have to be returned by this method but it is not true. Remembering that the interface have to be implemented by a collection, the method have simply to append the loaded items to the collection itself. So, this interface needs that the collection raises a CollectionChange event to update the user interface. An exaample is for sure much more clear:

   1: public class NaturalNumbers : ObservableCollection<int>, ISupportIncrementalLoading
   2: {
   3:     public bool HasMoreItems
   4:     {
   5:         get { return true; }
   6:     }
   7:  
   8:     public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
   9:     {
  10:         CoreDispatcher dispatcher = Window.Current.Dispatcher;
  11:  
  12:         return Task.Run<LoadMoreItemsResult>(
  13:             () =>
  14:             {
  15:                 int[] numbers = Enumerable.Range(this.LastOrDefault(), 100);
  16:                 
  17:                 dispatcher.RunAsync(
  18:                     CoreDispatcherPriority.Normal,
  19:                     () =>
  20:                     {
  21:                         foreach (int item in numbers)
  22:                             this.Add(item);
  23:                     });
  24:     
  25:                 return new LoadMoreItemsResult() { Count = 100 };
  26:     
  27:             }).AsAsyncOperation<LoadMoreItemsResult>();
  28:     } 
  29: }

If you attach an instance of this class to a GridView (or a ListView) it will show a series of natural numbers loading them incrementally when you scroll the control. Every time the GridView reach the edge of the screen it needs to load a number of items so, after having checked the HasMoreItems propertyit call the LoadMoreItemsAsync method and await for the end of the operation. Inside this method I start a new Task. This is required because I have to return something to wait to the caller and the AsAsyncOperation converts the Task to the requested async operation. Inside the thread I generate 100 numbers starting from last in the collection then I marshal this numbers to the ui thread and load them to the collection. Since the collection is Observable this updates the items in the user interface. Finally i return the number of items I generated.

As a more complex exercise I've prepared an example attached to the end of this post. This example use the incremental strategy to load images from Flickr search API. The application shown in the following screenshot implements the search contract. When a query is made it load a special collection and the items are loaded incrementally when the user scrolls the GridView.

screenshot_06102012_224133

For this purpose I've created a IncrementalSource class. This class implements the ISupportIncrementalLoading interface and is able to manage every data source that exposes a GetPage method. If you have this method already implemented in an application you can easily turn it to incremental loading in a breeze.

   1: public class IncrementalSource<T, K> : ObservableCollection<K>, ISupportIncrementalLoading
   2:     where T: IPagedSource<K>, new()
   3: {
   4:     private string Query { get; set; }
   5:     private int VirtualCount { get; set; }
   6:     private int CurrentPage { get; set; }
   7:     private IPagedSource<K> Source { get; set; }
   8:  
   9:     public IncrementalSource(string query)
  10:     {
  11:         this.Source = new T();
  12:         this.VirtualCount = int.MaxValue;
  13:         this.CurrentPage = 0;
  14:         this.Query = query;
  15:     }
  16:  
  17:     #region ISupportIncrementalLoading
  18:     
  19:     public bool HasMoreItems
  20:     {
  21:         get { return this.VirtualCount > this.CurrentPage * 25; }
  22:     }
  23:  
  24:     public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
  25:     {
  26:         CoreDispatcher dispatcher = Window.Current.Dispatcher;
  27:  
  28:         return Task.Run<LoadMoreItemsResult>(
  29:             async () =>
  30:             {
  31:                 IPagedResponse<K> result = await this.Source.GetPage(this.Query, ++this.CurrentPage, 25);
  32:                 
  33:                 this.VirtualCount = result.VirtualCount;
  34:  
  35:                 await dispatcher.RunAsync(
  36:                     CoreDispatcherPriority.Normal,
  37:                     () =>
  38:                     {
  39:                         foreach (K item in result.Items)
  40:                             this.Add(item);
  41:                     });
  42:  
  43:                 return new LoadMoreItemsResult() { Count = (uint)result.Items.Count() };
  44:  
  45:             }).AsAsyncOperation<LoadMoreItemsResult>();
  46:     } 
  47:  
  48:     #endregion
  49: }

In my example I've implemented a Flickr class that use the flickr.photos.search API. So when a search comes I create an instance of this collection in the ItemsSource property of the GridView.

this.gv.ItemsSource = new IncrementalSource<Flickr, FlickrPhoto>(search);

If you want to try this beautiful application please download the full code from the link below. Provide your own Flicks Api Key and run the example in Visual Studio 2012 RC.

Download: http://www.silverlightplayground.org/assets/sources/XPG.Examples.IncrementalLoading.zip

Categories:  
Actions:   E-mail | del.icio.us | Permalink | Comments (11) | Comment RSSRSS comment feed

Review: Mastering LOB Development for Silverlight 5: A Case Study in Action

2012-06-05T23:13:46+01:00 by codeblock

image

Braulio Diez, a friend of mine I known during the TechED 2008 in Barcelona, together with other authors has recently written a book about Silverlight 5. The "Mastering LOB Development for Silverlight 5: A Case Study in Action", published by PackLib is an interesting book that is able to point the light on the Silverlight topic, mixing together a good scan about Silverlight 5.0 features and a number of "cases study" that focus the attention of the reader on the power of this, still unmatched and irreplaceable, piece of technology.

In a time that seems to be pointed to the most new evolutions, inside the new version of Windows, mostly directly derived from Silverlight, reading this book may be a useful exercise to understand what it can do and what someone can still do using XAML instead of HTML5.

My thanks to Braulio for the pleasant reading.

http://www.packtpub.com/mastering-lob-development-silverlight-5/book

Categories:   Review
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed