Sony Arouje

a programmer's log

Populate Listbox Asynchronously using Await and Reactive Extension (Rx)

with 9 comments

Recently I completed rewriting one of my WPF application called iTraveller, one important area of interest was User interface. I did lot of things to make the UI responsive even in, time consuming operations. My first hurdle was populating the thumbnail images in a list box without halting the UI. If we populate Listbox in normal approach then the UI will be unresponsive till it completes. I searched a lot to find a better approach to populate ListBox asynchronously without halting my UI. Most of the approaches were too complex or not suited for my requirement. So I combined Async CTP’s await and Reactive Extension (Rx) to satisfy my requirement. The result was far far better than I thought, with my approach I could populate Listbox asynchronously with a very responsive UI.

Let’s see how I solved the issue.

First have a look at my Photo entity

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using Db4objects.Db4o;
namespace ITraveller.Domain
{
    public class Photo:IDisposable
    {
        public Photo()
        {
            this.ImageID = Guid.NewGuid();
        }

        private Guid _imageID;
        public Guid ImageID 
        { 
            get
            {
                return _imageID;
            }
            set
            {
                _imageID = value;
            }
        }

        private String _imageName;
        public String ImageName 
        { 
            get
            {
                return _imageName;
            }
            set
            {
                _imageName = value;
            }
        }

        private string _title;
        public string Title 
        {
            get 
            {
                return _title;
            }
            set
            {
                _title = value;
            }
        }

        private string _description;
        public string Description 
        { 
            get
            {
                return _description;
            }
            set
            {
                _description = value;
            }
        }

        private Uri _uri;
        public Uri Uri 
        { 
            get
            {
                return _uri;
            }
            set
            {
                _uri = value;
            }
        }

        private string _imagePath;
        public string ImagePath 
        { 
            get
            {
                return _imagePath;
            }
            set
            {
                _imagePath = value;
            }
        }

        private LocalCategory _category;
        public LocalCategory Category 
        { 
            get
            {
                return _category;
            }
            set
            {
                _category = value;
            }
        }

        private int _commentCount;
        public int CommentCount
        {
            get
            {
                return _commentCount;
            }
            set
            {
                _commentCount = value;
            }
        }
        private BitmapImage _thumbnailImage;
        public BitmapImage ThumbnailImage 
        {
            get { return _thumbnailImage; }
            set { _thumbnailImage = value; }
        }

        private BitmapImage _previewImage;
        public BitmapImage PreviewImage 
        {
            get { return _previewImage; }
            set { _previewImage = value; }
        }



        public void EmptyImage()
        {
            this.PreviewImage = null;
            this.ThumbnailImage = null;
        }

        public delegate void LoadImage();
        public Photo GetPhotoWithThumbnail()
        {
            this.ThumbnailImage = new BitmapImage();
            this.ThumbnailImage.BeginInit(); 
            this.ThumbnailImage.UriSource = new Uri(this.ImagePath); 
            this.ThumbnailImage.DecodePixelHeight = 50; 
            this.ThumbnailImage.EndInit();             
            return this;
        }

        public Photo GetPhotoWithPreviewImage()
        {
            try
            {
                this.PreviewImage = new BitmapImage();
                this.PreviewImage.BeginInit();
                this.PreviewImage.UriSource = new Uri(this.ImagePath);
                this.PreviewImage.EndInit();
                return this;
            }
            catch (Exception ex)
            {
                this.PreviewImage = null;
                return this;
            }
        }
   
        public void Dispose()
        {
            this.PreviewImage = null;
            this.ThumbnailImage = null;
        }

    }
}

It’s a normal Entity. GetPhotoWithThumbnail function creates the BitmapImage in the photo entity. Before adding to Listbox I make a call to this function to get the thumbnail image and bind it to ListBox.

I used db4o for persisting my data, I covered how I used db4o in one of my previous post. Let’s start the code walk through from Application layer. In my app layer I have a function to fetch all the photos belongs to a category.

public async  Task<List<Photo>> GetPhotosByCategory(LocalCategory category)
{
    return await TaskEx.Run(() =>
        {
            IRepository repository = GenericRepository.GetRepositoryInstance();
            return repository.Find<Photo>(p => p.Category.ID == category.ID).ToList<Photo>();
        }, CancellationToken.None);
}

 

I used await here to perform the data fetching task asynchronously.

Now let’s go to the Viewmodel and see how I am calling this function and populating the listbox. iTraveller implements MVVM model and as usual I used Caliburn Micro as my MVVM framework.

public async void LoadPhotosInCategory(LocalCategory category)
{
    PhotoService photoService = new PhotoService();
 
    //data retrieving call using await
    List<Photo> photos=await photoService.GetPhotosByCategory(category);
 
    //loading of List using Reactive extension
    IObservable<Photo> photosToLoad = photos.ToObservable<Photo>();
    photoToLoad.Subscribe<Photo>(p =>
    {
        Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action<Photo>(AddThumbnailToCollection), p);
    }, () =>
        {
            //this block will execute once the iteration is over.
        }
    );
}
private void AddThumbnailToCollection(Photo photo)
{
    photo = photo.GetPhotoWithThumbnail();
    this.Thumbnails.Add(photo);
}

 

public ObservableCollection<Photo> Thumbnails { get; private set; }

 

Dispatcher can be find in System.Windows.Threading namespace.

The observablecollection Thumbnails is binded to the Listbox in the View. That all happens behind the scene and done by Caliburn Micro.

I used Dispatcher here because the creation of BitmapImage should be in the same thread of the UI otherwise while binding it will throw error.

Try it out yourself and see how responsive your UI even if the Listbox is getting loaded with hundreds of thumbnails.

Advertisement

Written by Sony Arouje

February 15, 2011 at 6:24 pm

9 Responses

Subscribe to comments with RSS.

  1. […] This post was mentioned on Twitter by Sony Arouje, Sony Arouje. Sony Arouje said: Populate Listbox Incrementally using Await and Reactive Extension (Rx) http://wp.me/p12zd9-2B […]

  2. Hello! dcekcab interesting dcekcab site!

    Pharmk445

    March 14, 2011 at 10:39 am

  3. I’ve come across your post as I learn about Rx so my question reflects my incomplete understanding of Rx at the moment.

    Is there a reason you chose to explicity dispatch to the UI thread this way rather than use something like .ObserveOn(Scheduler.Dispatcher)? It’s recommended in the Rx Design Guidelines and Bart de Smet shows it in the presentation available on Channel 9 but maybe there’s a reason this pattern doesn’t fit here.

    bseddon

    April 28, 2011 at 4:45 pm

    • There is a reason I use Rx in that way. If you see in the CurrentDispatcher.Invoke I am loading bitmap image. If we create the Bitmap in dispatcher then we cannot bind it to the list control in UI, because the image created in background thread. So I have to create the image in UI thread, so used this approach. If you have any other approach, feel free to add it to the comment.

      Sony Arouje

      April 29, 2011 at 10:17 am

    • One of the main things that you get with lamda epnsersioxs that isn’t mentioned here is that the execution of the lambda expression is within the scope of the method which defines it. This means you can reference variables outside of the lamda expression within it, which can simplify some code.

      Gleice

      February 21, 2013 at 7:31 am

  4. Nice and thanks!

    imitacion rolex

    May 28, 2011 at 2:45 pm

  5. Very explanatory example. Thank you so much for this post.
    One question, could you explain how to add to the sample a cancelation mechanism for the asyncronous operation? I’m not quite sure how to do it this.

    Regards

    Sergio Navarro

    July 26, 2011 at 2:23 pm

    • Hi Sergio,
      Good to hear my post helped you. About the cancellation mechanism, I haven’t tried it. If time permits I will have a look at it and let you know about it.

      Sony Arouje

      July 26, 2011 at 4:10 pm

  6. Thank you. I went over my code 20 times trying to understand why my async/await or Task.Run() or BackgroundWorker code was failing me – leaving a blocked UI. I was only loading 200 items into a ListBox. You put me onto Rx and my resulting implementation rocks. Thanks so much for your time posting on this.

    Gord Cross

    July 15, 2017 at 5:02 am


Leave a Reply to Sony Arouje Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: