Sony Arouje

a programmer's log

Posts Tagged ‘await

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.

Written by Sony Arouje

February 15, 2011 at 6:24 pm

Using await in asynchronous programming

with one comment

In the recent release of VS Async CTP introduced a new way of placing async calls using await. I did a small analysis with the Async CTP. Based on my limited analysis I felt like the new CTP wont make much difference in making WCF calls from silverlight, only workarounds. In other hand the new feature is good to develop asynchronous API’s much clean and easier.

First lets see how we normally make async calls, for the demo I choose webclient as it is easy.

WebClient wc = new WebClient();
wc.DownloadStringCompleted += (o, e) =>
{
    if (e.Error == null)
    {
        //do our work
    }
};

wc.DownloadStringAsync(new Uri(“http://api.twitter.com/1/statuses/public_timeline.xml), null);

 

In the above approach the call will be done in single method. Then what’s the use of await, leme explain. Think about a scenario were, you wanted to call the twitter api and the function should return the result. The current scenario it’s not possible. The function will return back to the caller before the async calls completed. Here is were the await will come into picture.

I am going to rewrite the code using await. To use new CTP, I need to refer AsyncCtpLibrary.dll located in C:\Users\<UserName>\Documents\Microsoft Visual Studio Async CTP\Samples. Add namespace  system.threading.tasks

private static async Task<string> DownloadGoogleHomePageAsync()
{
    WebClient wc = new WebClient();
    var byteArr = await wc.DownloadDataTaskAsync(new Uri("http://api.twitter.com/1/statuses/public_timeline.xml"));
    return System.Text.Encoding.ASCII.GetString(byteArr);
}

as you can see above my new function will return the result of the async call. This approach eradicate the anonymous or lambda way of calling the async function. With this approach we can do an if condition check or what ever we need just like a normal function call.

I can call the above function as shown below.

string content = DownloadGoogleHomePageAsync().Result;

I am not going to explain the more technical details here. MSDN has a white paper giving an in depth knowledge of await and Task based programming.

Await In Silverlight

Await functionality is also available in Silverlight. I will be more happy if I could able to call a WCF service using await from Silverlight. Currently WCF wont serialize Task/Task<T>, so there is no direct way of calling await with a WCF service method. One workaround is mentioned in the msdn forum, I haven’t tried yet. I use Reactive extension to call async methods.

In one of Amadeo Casas blog mentioned that WCF team is working to adopt async model. I am awaiting for the WCF vNext release :).

Await in Real Life scenario

See my other post to see how to use await in real life scenario like doing a time consuming database call or doing a time consuming algorithm.

Written by Sony Arouje

November 30, 2010 at 7:20 pm

Posted in .NET, Silverlight

Tagged with ,

%d bloggers like this: