Earlier this week, the much anticipated Silverlight 2 was finally released! Personally speaking, it's a day that's been coming for a while - ever since my days prototyping the Core CLR engine running in the browser some two+ years ago now... and already, we're busy building the next layer of frameworks beyond the foundation provided by the CLR and the graphics/media engine and basic controls. Wow!
I'm working on one such effort of building an application framework on top of Silverlight these days at work, which as usual for any v1 product takes time. Over the past few weeks, I've also been working on updating Silverlight.FX to work against the final SL2 build, as well as adding a bunch of new goodies into the framework.
The area I want to talk about in this post is using and writing Silverlight controls that are effects and transitions enabled. This picks up from my last Silverlight.FX post that demonstrated procedural animations and declarative effects and transitions. The first iteration of this framework allowed an app developer to add some richer user interaction by associating effects and transitions to basic events such as clicks, hovers, focus etc. This post takes that one step further - higher level controls that intrinsically support such effects and transitions.
I have three controls to share in the context of two sample applications (screenshots below - click to run). The first application is Flickr Tiles - a basic Flickr browser (yes, I gotta have a photos sample), and the second is a simple Task List sample (yes, I've used the Task List scenario in a number of contexts in the past - what can I say - it's a nice simple scenario).
Static screen shots don't do justice to animations - so I'd encourage running the samples to get an idea of the application experiences I am talking about enabling via controls before reading on. You'll need to make sure you've installed the final build of Silverlight 2 if you haven't already.
ContentView
This acts and behaves like the regular ContentControl, but adds a ContentTransition property. You can set it to a CrossFade, Slide, Flip, Blinds, or Explode transition, or to a custom transition you build. The control automatically plays the chosen transition to switch from one object to the next when its Content property is changed. In the Flickr Tiles app, each time a photo is selected, the old photo explodes out (ala Dashboard on the mac) and the new photo fades in.
Here is an snippet of XAML markup demonstrating ContentView.
<fxui:ContentView x:Name="photoView" Content="{Binding SelectedPhoto}">
<fxui:ContentView.ContentTransition>
<fxtransitions:Explode Easing="QuadraticInOut" Duration="0:0:0.5" />
</fxui:ContentView.ContentTransition>
<fxui:ContentView.ContentTemplate>
<DataTemplate> ... </DataTemplate>
</fxui:ContentView.ContentTemplate>
</fxui:ContentView>
This shows how the ContentView control shares the Content and ContentTemplate property model that ContentControl provides, and adds the ContentTransition property, which is this case has been set to an Explode transition that plays over 0.5 seconds and uses a quadratic easing in and out behavior for a more natural animation.
TilePanel
This is an example of an animated panel that uses the same procedural animations framework to arrange child elements from one location to the next location in a smooth manner. The Flickr Tiles application uses this to arrange thumbnails. The framework also contains StackPanels that support animation and the Task List sample uses a VStackPanel to arrange the list of tasks. Here again, the framework is extensible - you can create your own animated layouts and just derive from AnimatedPanel to automatically tap into the procedural animation framework without writing your own animation code.
<ItemsControl x:Name="photosList" ItemsSource="{Binding Photos}">
<ItemsControl.ItemTemplate>
<DataTemplate> ... </DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<fxui:TilePanel TileHeight="103" TileWidth="103"
UseAnimatedLayout="True" Easing="QuadraticOut" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
ListView
This is a basic data-bound control that can be bound to an arbitrary collection. It is similar to the basic ItemsControl, but adds a couple of properties - ItemAddedEffect and ItemRemovedEffect (as well as other things like filtering and sorting). The ListView control plays the added effect for any item added, and plays the removed effect on any item being removed from the underlying collection before the corresponding UI is removed from the visual tree itself. The Task List sample uses a ListView to represent the list of tasks, and each time a task is added, the app plays a Web 2.0'ish yellow fade in effect, and each time a task is deleted, it plays a fade out effect.
The final snippet is the ListView control from the TaskList sample with a its effects properties initialized to a couple of out-of-the-box effects.
<fxui:ListView DataSource="{Binding Tasks}">
<fxui:ListView.ItemTemplate>
<DataTemplate> ... </DataTemplate>
</fxui:ListView.ItemTemplate>
<fxui:ListView.ItemAddedEffect>
<fxeffects:Highlight HighlightColor="#FFE396" Duration="0:0:1" Easing="QuadraticInOut" />
</fxui:ListView.ItemAddedEffect>
<fxui:ListView.ItemRemovedEffect>
<fxeffects:Fade FadeOpacity="0" Duration="0:0:0.75" Easing="QuadraticInOut" />
</fxui:ListView.ItemRemovedEffect>
</fxui:ListView>
In each case above you can see that you don't have to define storyboards, or event handlers to begin and stop storyboards. Instead you can use some pre-canned effects and transitions, and let the controls figure out when and how to play them based on the semantics of the controls. Clearly this is just the tip of the iceberg, in terms of both the set of stock effects/transitions as well as the controls that could use them.
You can download the framework and associated samples (full source to both included) to see not only how the samples were built, but also look and poke around as deep as you'd like inside the framework... and obviously use it in your own apps.
For those writing controls, you're probably interested in seeing what is happening beyond the XAML markup. Lets look at how effects and transitions can be used programmatically.
The first snippet is from the code for ListView, and demonstrates how an effect can serve as a factory or template of sorts for procedural animations, so that the same effect has to be applied to multiple items in the control.
private void AddItem(object dataItem, int index) {
Style itemContainerStyle = ItemContainerStyle;
DataTemplate itemTemplate = ItemTemplate;
ListViewItem item = CreateItem(dataItem, index, itemTemplate, itemContainerStyle);
if (item != null) {
_itemsPresenter.Children.Insert(index, item);
Effect addedEffect = ItemAddedEffect;
if (addedEffect != null) {
addedEffect.Target = (FrameworkElement)item.Content;
ProceduralAnimation addedAnimation = ((IAnimationFactory)addedEffect).CreateAnimation();
addedAnimation.Play(item);
}
}
}
The second is a bit of code from ContentView where the assigned transition is used to animate old content to new content.
private void OnContentChanged(object oldContent, object newContent) {
...
else {
Transition contentTransition = ContentTransition;
if (contentTransition == null) {
Panel panel = (Panel)_contentGrid.Children[0];
ContentPresenter contentPresenter = (ContentPresenter)panel.Children[0];
contentPresenter.Content = newContent;
}
else {
if (contentTransition.IsActive) {
contentTransition.StopEffect();
}
AddContent(newContent);
contentTransition.Target = _contentGrid;
contentTransition.PlayEffect(EffectDirection.Forward);
}
}
}
Reasonably straightforward to incorporate into controls ... hopefully.
The latest update of Silverlight.FX also contains a number of other early features, some of which will quite likely come up in future samples. These new feature areas include a dialog framework, and other controls. There are a number of updates as well - the behaviors and effects model has been revamped; triggers and actions have also been revamped to be much more flexible; script expressions in actions to bind view events to view model operations no longer depend on the DLR. Some bugs (incl. the big one around custom controls in the theming system) have been fixed.
If you've been following Silverlight.FX for some while, I'll recommend checking out the samples which are always up-to-date, as well as ask any questions you have below.