Image pre-fetch using Silverlight
Alright, this is my first blog, although I’ve been a developer for quite some time now… yeah I’m sorry for not following the ‘we share we improve’ policy. So I’ve created my blog site – ‘IBloggable – implemented’, and now.. off I go.
First, some ground rules: I’ll be writing about two and only two things
- Programming/Development and
- Anything else I want (heck yea.. its ma blog site.. can write wateva i want)
Now to the actual topic… last month I rolled out my website Photography & Eye after re-designing it in Silverlight, previously in Flash. The major changes I’ve done for the SL version are (on the UI side):
- to pre-fetch the images once the user chooses an album
- to have animations played while displaying the images
Playing animations… no big deal right? Just create your animations in Blend (awesome tool to develop WPF/SL apps) or dynamically in code and choose the animation based on a random value.
1: var rndTransitionChooser = new Random();
2: var currentRnd = rndTransitionChooser.Next(0, 49);
3: if (currentRnd < 9)
4: {
5: CenterWidthGrowHeight.Completed += CenterImageAnimation_Completed;
6: CenterWidthGrowHeight.Begin();
7: }
8: else if (currentRnd < 19)
9: {
10: CenterHeightGrowWidth.Completed += CenterImageAnimation_Completed;
11: CenterHeightGrowWidth.Begin();
12: }
13: else if (curentRnd < 29)
14: {
15: CenterFromTopRight.Completed += CenterImageAnimation_Completed;
16: CenterFromTopRight.Begin();
17: }
18: // and so on
Now, pre-fetch (downloading the images before the user requests it), was something that had me thinking. Initially, I had it to where the image gets downloaded after the user requested it. I told myself ‘why should the user have to wait for the image to get downloaded? Naaaah… let’s pre-fetch’. I put some conditions/guidelines for myself:
- When the user selects an album, pre-fetch 10 images (if album has < 10 images, pre-fetch all the images in the album… duh?)
- Anytime, (the total pre-fetched images - current viewing image) < 5, pre-fetch 5 more (making sure album images limit is not crossed… again… duh)
- Anytime, (the total pre-fetched images – current viewing image) <= 1, hide the ‘next’ navigation button; allowing for more images to be pre-fetched
- Once all images of the album have been pre-fetched, allow user to navigate in either direction with looping (if the user clicks ‘next’ on the last image, loop back to the first one and so on).
- Lastly, give some indication to the user that something IS happening in the background.
Time for some code now.
To get a resource (the image itself), I used the WebClient class (System.Net.WebClient)
1: // use a WebClient instance to download content
2: var webClient = new WebClient();
3: webClient.OpenReadCompleted += WebClient_OpenReadCompleted;
4: webClient.OpenReadAsync(new Uri("<path for the resource>"));
and the WebClient_OpenReadCompleted event looks like:
1: private void WebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
2: {
3: try
4: {
5: // read the result as a BitmapImage
6: var bitmapImage = new BitmapImage();
7: bitmapImage.SetSource(e.Result);
8:
9: // downloadedImages is of type List<BitmapImage>()
10: downloadedImages.Add(bitmapImage);
11:
12: // increment the counter
13: // that tracks pre-fetched images
14: currentDownload++;
15:
16: }
17: catch(Exception ex)
18: {
19: // show message
20: }
21: }
It’s worth mentioning that I’m tracking how many images have been pre-fetched and the current image the user is on. This basically helps in taking care of conditions 1 and 2 with some additional plumbing.
Moving on, once the user clicks on one of the navigation buttons (previous or next), I hide both the buttons. Here are the rules:
- if (the total pre-fetched images – current viewing image) > 1, show the next button (this is condition 3)
- if this is not the first image or if all images of the album have been pre-fetched, show the previous button (this is condition 4 – allows looping through the images)
Condition 5 was easy too. I wanted it to look like the progress bar - with an outer rectangle to mark the border and an inner one to show the ‘fill’ (hey that’s condition 6 right there). I also added a nifty little tooltip to tell the user what this blue bar was.
1: private void SetToolTip()
2: {
3: if(currentDownload == imagesList.Count)
4: {
5: ToolTipService.SetToolTip(OutlineRectangle, "Buffered full album");
6: ToolTipService.SetToolTip(BufferRectangle, "Buffered full album");
7: BufferRectangle.Width = 70;
8: NextButton.Visibility = Visibility.Visible;
9: PreviousButton.Visibility = Visibility.Visible;
10: }
11: else if (currentDownload <= currentIndex + 1)
12: {
13: ToolTipService.SetToolTip(OutlineRectangle, "Buffering...");
14: ToolTipService.SetToolTip(BufferRectangle, "Buffering...");
15: var width = (currentDownload - currentIndex)/10.0*70;
16: BufferRectangle.Width = width > 70 ? 70 : width;
17: NextButton.Visibility = Visibility.Collapsed;
18: PreviousButton.Visibility = Visibility.Collapsed;
19: }
20: else
21: {
22: ToolTipService.SetToolTip(OutlineRectangle, string.Format("Buffered {0} images", currentDownload - currentIndex));
23: ToolTipService.SetToolTip(BufferRectangle, string.Format("Buffered {0} images", currentDownload - currentIndex));
24: var width = (currentDownload - currentIndex) / 10.0 * 70;
25: BufferRectangle.Width = width > 70 ? 70 : width;
26: NextButton.Visibility = Visibility.Visible;
27: // since pre-fetch is done only in the forward direction
28: // hide the previous button when the currentIndex = 0
29: // but if all the images in the album have been downloaded
30: // display the previous button even at currentIndex = 0
31: if (currentIndex == 0 && downloadedImages.Count != imagesList.Count) return;
32: PreviousButton.Visibility = Visibility.Visible;
33: }
34: }
See the screen captures of the below. The tooltip comes up when the user does a mouseover on the blue bar.
There it is. That’s how I’ve done pre-fetching. There’s one more thing I’d like to do in this regard. When the next button gets hidden, I’d like to play an animation to visually indicate to that the next button will be displayed in a few moments. That’s planned for 2037 AD… just kidding.
That also marks the end of my first blog.
Thanks and please post comments.
Arun