Share via


A simple, dynamic, picture show with WPF/E

One nice little demo application that you can put together with WPF/E is a simple picture slide show.

There are lots of ways that this can be done, and here is a method where you can do it, just using a single animation definition for a number of pictures.

First, here's the XAML:

<Canvas xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" Loaded ="javascript:Canvas_Loaded"> <Canvas.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard> <Storyboard x:Name="FadeIn" BeginTime="1" Completed="javascript:AnimCompleted"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="back" Storyboard.TargetProperty="(UIElement.Opacity)" > <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/> <SplineDoubleKeyFrame KeyTime="00:00:01" Value="1"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Canvas.Triggers> <Canvas MouseLeftButtonDown="javascript:Image_Clicked"> <Image x:Name="front" Width="1024" Height="597" Canvas.Left="0" Canvas.Top="0" Opacity="1" Source="assets/slide1.jpg" /> <Image x:Name="back" Width="1024" Height="597" Canvas.Left="0" Canvas.Top="0" Opacity="0" Source="assets/slide2.jpg" /> </Canvas></Canvas>

 

 As you can see, it's pretty straightforward, containing only 2 images, called 'front' and 'back'. WPF/E renders elements in the order that they are parsed, so, in this case 'back' is rendered on top of 'front', but back has its opacity as '0', so it's invisible. The images are considered to be called slide1.jpg, slide2.jpg etc.

The animation target's the opacity of 'Back', moving it from 0 to 1 over 2 seconds, causing 'back' to fade in, and over 'front' when the animation is started.

The Canvas containing the images has a click event handler defined, called 'Image_Clicked' -- thus if you click on either of the images in the canvas, this will be tripped. The animation has a completed event handler called AnimCompleted that will fire when the animation finishes.

Finally, the animation is defined with a BeginTime of '1' -- this is because there is no way (as yet) of triggering an animation at a fixed time -- it is done in response to a triggered routed event, and the one that is relevant is the 'loaded' event, so, the workaround here is to trigger it on loading the framework, but don't start it for a very long time -- in this case 1 day. However, from script you can trigger it, and you'll see that in the JavaScript code in a moment. Finally, teh Canvas defines a 'Canvas_Loaded' event handler which will fire when the canvas loads.

Now, let's look at the JavaScript:

First, here's a bunch of Global variables:

var currentItem = 0;
var nextItem=0;
var picFront;
var picBack ;
var anim;

We'll see how these are used as we go through the code

Ok -- here's the Canvas_Loaded event handler:

function Canvas_Loaded(sender,args)
{
currentItem = 1;
nextItem=2;
}

This just initializes everything, setting the current item to '1' and the next item to '2'. Remember the 'front' picture is currently being shown -- and it defaults (in the XAML) to slide1.jpg.

Next up, we'll see what happens when you click on an image:

function Image_Clicked(sender,args)
{
anim=sender.findName("FadeIn");
anim.Begin();
}

This is pretty straightforward -- WPF/E offers a 'findName' API function that allows you to find an element in the page. So, when we click on an image, we find the 'FadeIn' animation and start it running.

When it completes running, it calls the AnimCompleted event handler:

function AnimCompleted(sender,args)
{
picFront = sender.findName("front");
picBack = sender.findName("back");
currentItem = nextItem;
nextItem++;
if(nextItem==11){
nextItem=1;
}
var picCurrent = "assets/slide" + currentItem + ".jpg";
var picNext = "assets/slide" + nextItem + ".jpg";
picFront.setValue("Source", picCurrent);
picBack.Opacity = 0;
picBack.setValue("Source", picNext);
}

Here's where the smarts are -- let's take a look at what happens when this runs.

First, it creates references to the front and back images, called 'picFront' and 'picBack'. It then sets the currentItem to the nextItem, incrementing it by 1, or looping it back to the start if nextItem is back there. Then, it increments nextItem. In this case I'm cycling through 10 images called 'slide1' through 'slide10'. So, when nextItem becomes 11, I reset it to 1.

Next I set the picCurrent and picNext vars to the URI of the relevant pictures (i.e. assets/slide2.jpg and assets/slide3.jpg).

At this point, the animation is completed, and the back picture is overwriting the front picture, so, if we set teh Source of the front picture to the next pic in the sequence (was previously the back), and now set the opacity of the back to 0, we don't see any difference. Finally, now that the back is invisible again, we set its source to the next picture, and upon clicking the image the sequence starts again.

And that's it! A simple dynamic slideshow, defining only 1 xaml animation to cover 'n' pictures. Perhaps, dear reader, you could consider extending on this to consume a number of pictures from a known list (such as the results of a flickr call) instead of hard-named ones, or something else equally creative. As I play with it, and evolve it, I'll continue to post here! :)