Exploring the Microsoft Client Continuum

Loading Dynamic Images

Let's leverage some of the techniques we've been learning and build a slightly more advanced Silverlight project, a dynamic photo gallery. There are multiple approaches to building a similar photo gallery in Flash, all of them well documented online (See Additional Resources below). Also, we will be introducing some new Silverlight techniques that don't have an obvious Flash corelation. So we will forgo the Flash example for this lesson and jump straight into the Silverlight project. But we will be using Photoshop instead, to keep things interesting!

Here is the Silverlight Project we will be building:

Get Microsoft Silverlight

Part 1 - Building the Interface

As you can see, I wanted the project to have some shadowing effects and a more intricate background. So I created two .PNG images from Photoshop like so:

The first file lesson05_bkg.png will serve as the background image and the second file lesson05_cover.png will server as our transparent cover that will give us the desired inner shadow effects.

Create our Silverlight solution in Visual Studio and import these two images into the project. After we do the typical modifications to Page.xaml we've been doing for every project (sizing the control, adding a Loaded event handler to the User Control tag, changing LayoutRoot from Grid to a Canvas), we're ready to add the images to our stage. We will add lesson05_cover.png by using an Image tag like the ones we used in Lesson 04. But let's introduce a new technique, the ImageBrush, to paint the lesson05_bkg.png image directly onto the background of the LayoutRoot canvas instead. The code for all these modifications looks like this:

The decision to use an Image tag versus painting the image directly onto the background of another element using an ImageBrush is a subtle one and often determined by programmer preference. However, there are differences between the two and experimenting with both of them is the best way to become familiar with what each has to offer. You should note that besides ImageBrush, Silverlight also provides a GradientBrush and a very powerful VideoBrush that lets you paint a playing movie directly onto other elements as well!

We're going to add three more elements to Page.xaml. An empty Canvas tag named thumbBar that will hold our collection of thumbnails we create dynamically. An empty Image tag named photoFrame that will display the full sized image when we select the thumbnail. And a basic Rectangle named locator that will slide above our thumbnails to indicate which image is being displayed.

We will also add a Storyboard named sbMoveLocator that will control the sliding animation for locator. I opened this project in Blend to finesse some nicer KeySpline values for the Storyboard to make the animation ease locator to its target more gracefully. We will also introduce another powerful trick for combining XAML Storyboards and animation code. Notice that we've given a name of locatorTarget to the SplineDoubleKeyFrame property of our Storyboard below in Line 11. This will let us programatically change the target value of the Storyboard in code but still leverage Blend and the ease of markup for all the rest.

This new Storyboard and the three additional elements now make our Page.xaml look like this:

Next, let's add a new User Control to our solution and name it thumb.xaml. This will serve as both the User Control that loads its designated photo from the server and the visual thumbnail for that photo as well. Set the Width to 126 and the Height to 78 on the User Control. We don't need to add a Loaded event this time. After changing LayoutRoot to a Canvas and removing its background value, we need to add another Canvas tag within it named bkg with a Width of 126, Height of 78, and background set to #444444. Add an empty Image tag named photoThumb of the same dimensions that has its Opacity set to zero and Stretch set to Fill.

We will adjust the height of bkg progressively as we load each image into its thumb.xaml container to give us a vertical progress bar. Once each image is loaded, we will set it as the source for photoThumb to have it appear on screen. However, let's make it a little more elegant than just popping into view once loaded by adding a Storyboard to our XAML that will fade up the Opacity of photoThumb once called. Therefore, our final script for thumb.xaml looks like this:

The three additions we'll make for now to the code behind file thumb.xaml.cs are the X and Y public properties we've been using to all our User Controls to make positioning easier, as well as a public integer called index. We also need to add some library references we'll be using once we start working with the actual images:

Although we haven't added any image logic to our thumb.xaml user control, we have enough to start testing out our positioning logic and indicator animation back on Page.xaml. So let's go to the code behind file Page.xaml.cs and start adding these User Controls to the thumbBar canvas once Page.xaml is loaded.

Here is the updated code:

In Line 12, we added a using reference to the Imaging library since Page.xaml.cs will also be displaying images as well. At Line 20, we create a reference for whichever thumb.xaml control will be the currently selected one. The value of this will be made apparent when we start loading images a little later.

Line 24 is an interesting one and a common gotcha when dealing with transparent PNG files. Since thumbBar is under our cover Image to take advantage of the drop shadow effect, we need to make cover invisible for any hit testing so as not swallow the MouseLeftButtonDown() events we want to add to to the thumb objects in Line 30. Hopefully, the instantiation and positioning logic of Line 25 through Line 32 is starting to look familiar.

Line 35 through Line 40 defines the handler for the MouseLeftButtonDown() event we added in Line 30. The first thing it does is store a reference to the thumb that was clicked in our thumbToShow variable in Line 37. Remember how we gave a name of locatorTarget to the target value of our sbMoveLocator Storyboard back in the Page.xaml? Line 38 and Line 39 lets us take advantage of this now by quickly changing the target value of the Storyboard before calling it to slide locator to its new position. This is a nice way to mix programatic code with XAML Storyboards and doesn't force us to build our own animation engine for a basic sliding effect with easing we can quickly compose in Expression Blend.

At this point, if you run the code you will see our application UI in place and hopefully you can click on any of the grey thumb tiles and have locator slide to the correct position above it. Check your code against the code above and make sure any bugs are resolved before moving on.

Part 2 - Adding the Imaging Code

The majority of the imaging code needs to get added to the User Control thumb.xaml.cs and looks like this:

The main function is defined on Lines 38 - 47 and named loadPhoto(). It is called from Page.xaml.cs after each thumb instance has been positioned and had its public index variable (Line 34) set to a unique number in our source photos naming sequence. In the Dragging, Easing & Inertia Lesson, we used a URI and BitmapImage() to create an image source for our Image object. We could repeat that same process here. However, we want to visually represent the progress of the images as they each load from the server, so let's use the more powerful WebClient object (Line 41) instead.

To do this, we attach an OpenReadCompleted event handler (Line 42) and a DownloadProgressChanged event handler (Line 44) to our WebClient object named imgLoader. Line 46 has imgLoader begin the actual file download request by passing in the photo's location relative to the page as a URI in the OpenReadAsync() command. (You can get the image for your applicaiton by downloading the images below.)

Starting at Line 50, we have our DownloadProgressChanged() event handler. We only need one line of code for our loading bar animation, since we are setting the Height of our bkg canvas to reflect the progress percentage as it changes.

The rest of the work takes place once imgLoader finishes loading the file from the server in the OpenReadCompleted handler (Line 56). We should check to make sure we've aquired a valid result by querying the OpenReadCompletedEventArgs variable we receive, e (Line 58). The actual photo data is in the stream referenced by e.Result. That is why we created a public System.IO.Stream variable in Line 36 called image to store a copy of this photo stream and make it available to Page.xaml later (Line 63). We want to use this photo stream data to also create the thumbnail, though, so the rest of the function does that. We turn the e.Result data into a valid BitmapImage() and set it as the Source of our photoThumb Image in thumb.xaml. Since we initially set photoThumb's Opacity to zero, once we have given it a valid Source in Line 65, we can play the Storyboard sbShowThumb to fade it up.

Now lets add some code to Page.xaml.cs to take advantage of these changes in thumb.xaml.cs. Before we do, though, there are two more Storyboards I want to add to Page.xaml as shown below that will control the transition between images whenever we want to display a large copy of the photos we just loaded. They are named sbShowPhoto (Line 8) and sbHidePhoto (Line 14). Together, they control the Opacity of our photoFrame image (Line 36). Also note that sbHidePhoto has a Completed event handler which we will write up next.

Here is the final Page.xaml with the two additional Storyboards added:

And the changes we need to make to Page.xaml.cs are the following:

Line 36 calls the loadPhoto() function we created in thumb.xaml.cs. The only addition needed in the MouseLeftButtonDown handler for our thumbs is shown on Line 43, where we start the sbHidePhoto Storyboard to fade out any image currently on screen after we store a reference of the thumb that was clicked in our variable thumbToShow. Once this Storyboard is complete and photoFrame is completely transparent, the Completed event handler will get fired (Line 48). Now we can use our updated thumbToShow reference to use the image stream we saved in each thumb and set it as the Source for the larger photoFrame Image tag in Page.xaml. Once we've updated the photo to display in photoFrame, the only thing left to do is fade it back up by calling sbShowPhoto.Begin() in Line 53.

So in this lesson, we combined many of the techniques we've been learning into a larger Silverlight photo gallery application. We also introduced ImageBrush and WebClient, two powerful objects we have at our disposal. However, there are still many more features to explore. In our next lesson, we will compare Flash masking with Silverlight clipping and OpacityMask features.

Additional Resources

You can find many Flash photo gallery tutorials online. Here is one for Actionscript 1.0:
http://www.kirupa.com/developer/mx2004/xml_flash_photogallery.htm

and one for Actionscript 3.0:
http://www.flashmagazine.com/tutorials/detail/as3_photo_gallery/

Here is a good introduction to VideoBrushes in Silverlight:
http://msdn.microsoft.com/en-us/library/bb404773.aspx

And a nice example of animating video using a Silverlight VideoBrush:
http://dotnetslackers.com/articles/silverlight/Animation...Silverlight20Beta4.aspx

Have a question or comment on this article? Have a suggestion for new content?
Send your feedback.
Microsoft Communities