Sony Arouje Blog

a programmer's log

Posts Tagged ‘WPF

Populate Listbox Asynchronously using Await and Reactive Extension (Rx)

with 8 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

iTraveller–A photo uploader for Flickr and Facebook

with 5 comments

 

Download iTraveller

Today I released my new version of iTraveller. I was working on this version for last 2 weeks, it’s a complete rewrite of old iTraveller, with several added usability features. Now iTraveller can upload photos to Facebook as well.

What is iTraveller

iTraveller is a desktop application to organize your photos and upload them to Flickr and Facebook. iTraveller ease the organizing of photos. We can mark which photos should go to Flickr or Facebook and add to appropriate set/album in offline. Once you decide to upload, click the Synchronize button leave the rest to iTraveller. While iTraveller synching with Flickr/Facebook, you go for a walk or grab a cup of coffee or you can continue creating your local category or adding more photos to Flickr Set/Facebook Album. Another useful feature of iTraveller is, you can see the photo comments posted by other users in offline mode.

I used Calburn micro to separate my View and View Model. Also I extensively used Async CTP to make a very responsive UI. I will give more details of my architecture in a different post.

Technologies used

  1. WPF
  2. Caliburn Micro
  3. Async CTP
  4. MEF
  5. Facebook SDK
  6. Flickr SDK
  7. Reactive Extension (Rx)
  8. Lucene .NET
  9. db4o

 

Let’s see some screen shots of iTraveller

Home Screen

home

First step in using iTraveller is creating a Local set. You can create a local set by dragging and dropping your photos from Windows explorer to the Local Set box at the left top pane. When you drag and drop photos to Local Set pane, it will ask whether you need to create a new set or add to an existing category. You can see the photos inside a category by clicking on category and all the photos inside the category will show in the bottom pane of the application.

Humans are prone to errors, if you accidentally create any Local Set, don’t worry select the Local Category and just press delete key in your keyboard, you are done. The local set is deleted (only from iTraveller). In some scenarios you may need to delete only couple of photos from a local set. You can do that as well, select the photo from the thumbnail view and press delete key in your keyboard.

Mark for Flickr Uploading

You need to authorize iTraveller to connect to Flickr. Clicking on the synchronize button for the first time will popup a dialog box and follow the step to authorize it. After the authorization iTraveller will download the Flickr sets from Flickr.

Now let’s see how we can upload photo(s) to Flickr. Select a Flickr set in the right hand side of the application. Click on the Flickr set you want to upload your photo. It will open a tab as shown below.

FlickrUploadBasket

Drag the required photo from the thumbnail to the Queue box and drop it. You also can give the Title and Description for the photo. By default visible to public is unchecked, if you want your photo to be visible to public then check the ‘Is Visible to Public’. You can put as many photo to queue. Once you are connected to internet, click the synchronize button just below the Flickr set to upload it to Flickr.

Mark for Facebook Uploading

You can upload the photo to Facebook just like the way we did for Flickr, instead of Flickr set you should select a Facebook Album on the right hand side. First time synching with Facebook needs your authorization. After you allow iTraveller to communicate with Facebook account, it will download all your Facebook Albums. iTraveller will take some time to get the photo albums from Facebook.

FacebookUploadBasker

Once the synchronization is over and iTraveller got any new comments from Facebook or Flickr then it will open an Updates tab for you, so that you wont miss any updates.

 

You can provide different title and description for Facebook and Flickr Photos. These text is also searchable.

Create Flickr Set Through iTraveller

You can also create Flickr Set through iTraveller. Click on the ‘+’ icon, just above the Flickr Set Thumbnails. iTraveller will popup a window to enter new Flickr Set and Set description, click save once you done. While synchronizing with Flickr, the set will get created in Flickr. To create a Flickr Set successfully, at least one photo should be their in the upload queue of the newly created Set. The thumbnail of the newly created Set will be displayed only after the synchronization.

Search

iTraveller have built in searching feature. It enables the user to search for photos that satisfy the search text. The search will only look for photos added to iTraveller. User can search for the photo name, Title, Description, Flickr Title, Flickr Description, Facebook Description and Photo Comments. Enter the Search Text at the text box located at the top right corner of the application.

As a photographer you will have a lot of photos in your hardisk. Always our big problem is how to find a photo with some attributes. iTraveller can help you out, you can add a title and description and just save it. When ever you want to find it just enter the search text and click search. Another reason for search is, you may wanted to find the photos that got comments from your friends (either from Facebook or Flickr), let’s say Kevin or Sony. Go ahead and enter Kevin;Sony in the search text and hit Search button. iTraveller will give you the list of photos commented by Kevin or Sony.

SearchResult

You can see the preview of the Searched photo by double clicking on the thumbnail.

Comments

In the preview if any photo have any comments then a View comments button will shown in the preview tab as shown below.

CommentButton

Clicking on the comments button will open a new comment tab as shown below. You can leave this tab open and the comments will get changed when you navigate from one photo to another through the thumbnail.

CommentView

Preview Tab

Preview tab will display the image in preview mode. Below the preview image, you can see below buttons

image

Fullscreen-64

Clicking on the button will show the photo in Full screen. The background of Full screen mode is 18% gray. 

user-comments-icon

View Comments button, this button will be visible only if the preview photo have any comments.

thumbnail

By default the Local Set thumbnail is randomly picked. Any time you can change the thumbnail image of a Local set. Select the category and select the desired photo from the thumbnail. In the preview tab click on this button. Done… your Local set thumbnail is changed instantly.

notes-edit-icon

Note button opens a small popup and you can add a Title and Description to your photo.

 

More feature will get added in next release, but needs motivation. Use my iTraveller and Motivate me Smile.

Upgrade to New Versions

In coming days I will release iTraveller with bug fixes and new enhancements.

If you have already installed iTraveller 2.0, then you can upgrade to latest version. Please take a backup of the following files and folders before you update to new version, it will help you keep your data safe. In case of any issue after the update like not showing your Local set or any thing, you always can put the backuped up files back to the iTraveller folder.

You can find the the below files/folders here ‘Program Files\iTraveller\iTraveller’

Files/Folders to Backup

  1. ITravellerData.data  (File)
  2. FacebookThumbnails  (Folder)
  3. FlickrThumbnails  (Folder)
  4. SearchIndex  (Folder)

 

Download

I uploaded the application here. Install it and let me know your feed back.

Written by Sony Arouje

February 13, 2011 at 10:53 pm

Follow

Get every new post delivered to your Inbox.

Join 173 other followers

%d bloggers like this: