In this lesson we'll refine our search results page by animating the images as they are
loaded from Flickr. We want the images to ease in, fade in to view. We also want to
provide feedback to the user of the app for long running operations like retrieving these
photos over a slow internet connection by adding a progress indicator. And finally, I
want to add some feedback in case the user searches their current location and topics
and Flickr has no results to return ... we don't want to leave the user in the dark waiting
for photos to appear that never come.
So, our game plan in this lesson:
1. We'l modify our LongListMultiSelector's DataTemplate -- we'l prepare the Image control for the fade in effect we'l implement
2. We'l create an animation and storyboard targeting the image control to enable the fade in effect
3. We'l make provisions for the possibility that there are no photos on Flickr that match our search criteria by creating and hiding a Text block that we'l un-hide when there are no photos to show
4. We'l add a progress indicator that wil run while we're performing the web service call to Flickr and loading the images
1. Prepare the Image control for a fade-in animation
The first step we'll take is to set the initial Opacity attribute of the Image to 0, indicating
that we want to hide the contents of the image. 0 is completely transparent and 1 is
completely opaque. 0.5 would be partially transparent / opaque.
Then, for each image in our DataTemplate, as that particular image has downloaded
from Flickr, the ImageOpened event will fire. We'll handle that event and write code to
animate the change of the Opacity attribute.
So, we'll make the following changes to the Image control:
1. Add an Opacity attribute, set it to 0
2. Add an ImageOpened event handler attribute and wire it up to a new method called "Image_ImageOpenend", which we'l implement in the next step.
Right-click anywhere on that line of code with the ImageOpened attribute or it's setting,
select "Navigate to Event Handler" from the context menu.
2. Implement the Image_ImageOpened Event Handler
Here we'll write C# code to animate the fade in effect. Many examples of animation
you'll see use XAML to define both the Storyboard and Animations associated with the
storyboard, then use C# code to kick off the animation when a certain event is triggered.
We could do it that way, but we've chosen to do it all with C# because we're dealing
with a special situation (more about that in a moment).
Animations are made up of a Storyboard object and at least one Animation object.
Let's start with the Animation. First, there are several different types of animation data
types. We're using a DoubleAnimation data type because we want to move a property
from 0.0 to 1.0. There's also a ColorAnimation class for animating between two colors,
and a PointAnimation for modifying an objects X Y coordinate or it's size.
An Animation is basically a timeline combined with a result. The results are determined
by the specific Animation class you pick like we just talked about. It's important to know
that all Animation classes inherit from a Timeline class which confers properties related
to timing of the animation ... the Begin time, allowing you to delay the start of animation,
perhaps waiting for other animations on the storyboard to begin or complete, a Duration
property that effects how long the animation should take before delivering the desiredresult -- how long should it take for our fade in effect, in this case. There's also
AutoReverse and RepeatBehavior properties which do what they suggest.
A Storyboard is a collection of one or more Animations. You group the Animations you
want triggered by a specific event, like a button click, a loaded event and so on. The
Storyboard allows you to pair an Animation with a target object. The animation is just a
definition of what property should be affected, when it should be affected and how long.
We have to APPLY that Animation to a target object. In our case, that target object will
be an Image control.
In our case, our Storyboard is simple ... we just want one thing to happen. We could add
multiple Animations targeting multiple properties of multiple objects, and could even
create child Storyboards to better refine how our user's experience will play out. Once
we're ready to allow the Storyboard to play, we call its Begin() method. It will in turn kick
off all it's child Animations and child Storyboards.
For a more complete explanation of animations, I'd recommend you start here:
Back to our situation ... we need to animate each image as it's opened. We'll create an
Animation to move the Opacity property from 0 to 1 thereby making it transform from
completely transparent into completely opaque. We'll add the Animation to a Storyboard and then call Begin() on the Storyboard.
Sometimes the animation is subtle to the untrained eye. To make the effect more
dramatic, you can stretch the time from 500 milliseconds to something like 2000 or 3000 milliseconds, or rather, 2 or 3 seconds just for testing purposes.
Lines 74 and 75 use a SetTarget and SetTargetProperty syntax to pair up the animation and its target. You see a lot of Set___ and Get___ styled methods in the Windows Phone API, and that's due to the Windows Phone property system we talked about at the very outset of this series.
The Windows Phone property system allows us to use attached properties. If lines 68
through 75 were in XAML, it might look something like this:
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ImageControlName"
Storyboard.TargetProperty="Opacity"
To="1.0" Duration="0:0:0.5" />
</Storyboard>
(Now, this approach is actually flawed because we would be targeting a single Image
control. And as far as I know, you can't use a binding expression inside of the
Storyboard.TargetName to make it dynamically apply to every image control in our data
template, so this is one reason why a C# approach works better.)
I wanted to show the XAML version (flawed though it is) to illustrate what we're doing in
our C# code ... in lines 74 and 75 we're ATTACHING the Storyboard.SetTarget and
Storyboard.SetTargetProperty attached properties TO the DoubleAnimation object
doubleAni, and setting their values appropriately. It looks like we're setting attributes of
the Storyboard, but we're ATTACHING attached properties TO the DoubleAnimation.
One more curiosity about line 75. We're setting the Storyboard.TargetProperty to a new
PropertyPath(OpacityProperty) ... what does this mean?
The rules of the Windows Phone property system require the target property of an
animation must be set to a Dependency Property. Remember: that's one of the special
powers of a Dependency Property -- that it can be animated, unlike regular old CLR
properties. Fine, then why do we have to wrap it with the new PropertyPath object.
That's difficult for me to explain, and for the sake of brevity let me point you to another
article that can explain it with a good example:
PropertyPath XAML Syntax
Specifically, this link points to the anchor "PropertyPath for Animation Targets"
In a nutshell, the PropertyPath is used to specify the property that is the target of an
animation. We have a simple case, and so for now, I think it's easier just to understandthat the PropertyPath object provides a map to find the dependency property we want to animate.
Let's make sure our images fade in by debugging the app:
Mine works, and did you notice the half-second fade in? Very nice.
We've satisfied the topic of this lesson, but I wanted to add a few more features to our
app while we're here. We'll provide feedback to the user to let them know the status of
their searches against the Flickr API.
3. Add a TextBlock for "No Photos Found", a Progress Bar and TextBlock for "Loading"
Now, lets edit the MainPage.xaml again, sandwiching the LongListMultiSelector with
two new passages of XAML:
1. We create a TextBlock element. A TextBlock is a simple XAML element for displaying small amounts of flow content. By "flow content" I mean textual content that dynamically adjusts and reflows the text content based on run-time variables such as window size, device resolution and other user preferences. There's quite a bit to Flow Documents in XAML ... I would point you to this resource for more information: http://msdn.microsoft.com/en-us/library/aa970909.aspx At any rate, the TextBlock is just a way to put some text on our page. We style it using a built-in Text Style: http://msdn.microsoft.com/en-
us/library/windowsphone/develop/ff769552(v=vs.105).aspx#BKMK_TextStyles and most importantly: we set it's Visibility attribute to Col apsed meaning we want it hidden by default. We'l write some logic that will potentially change that in a moment.
2. Add a StackPanel that contains another TextBlock. It also has a ProgressBar element with an IsIndeterminate="True" set, meaning we just want it to keep animating until we're finished with it. We'l toggle this property on and off as we need it. Most importantly, we set the Visibility of the StackPanel to "Collapsed" ... here again, we'l write some logic that will toggle the Visibility as we're performing the web service call in the background.
Next, in the SearchResults_Loaded event handler, we'll write the logic for both code
passages (above) that I just alluded to.
We added code before and after our web services call to Flickr to show and hide the
StackPanel's contents, as well as toggle the ProgressBar's animation.
1. Here we show the StackPanel we added (named Overlay). Also, we turn on the ProgressBar's animation by setting the IsIndeterminate = true.
2. In lines 47 through 50, we decide to toggle the Visibility of the TextBlock to reveal the run stating "No Photos found :(" in, in fact, no photos were returned from the Flickr web service call.
In lines 52 and 53, we reverse what we did in lines 34 and 35 ... we toggle the Visibility
back to Collapsed to hide it, then set IsIndeterminate = false to stop the animation.
I'll test both scenarios. First, I'll search for some non-sensical term:
Since it takes a moment to run the query, depending on the speed of your internet
connection, you should briefly see the "Loading ..." text and the animated progress bar.
Ultimately, we want to see the results:
And it works!
Recap
Just to recap, the big take away from this lesson is animation in the Windows Phone
API. We create an Animation object to control the change of a dependency property
over time, and pair that Animation object with a dependency object and add that pairing
to a Storyboard. These little touches, almost imperceptible, make our apps stand out
from the competition and earn the respect of our users.
We also added feedback to the user about the state of the app while it's making that
long call to the Flickr API with a Progress Bar and a message, and let the user know
when there were no Flickr images that matched their criteria. Again, we're trying to
consider this from a user's perspective ... I've used plenty of apps where I wasn't sure if
the app was still working or it had locked up. These little touches help the user gain
confidence in the app and enhance their emotional ties to it.
No comments:
Post a Comment