Last week I had opportunity to really dive into some fun with a Silverlight 2 ListBox. It all started with a requirement for a simple Scroller control. You know, the simple left-to-right scrollers that you see in many applications? I’ve seen them in several places, yet when I searched around for a Silverlight version that met my criteria I came up empty. I have seen many great Image Carousel controls around, but overall the majority of them were not quite "calm enough" for a typical Line of Business application and I wasn’t really intending on using images only. There were some really great "pieces" that I found as I attempted to assemble this control and now that I have a functional version, I thought I’d share it with the community.
The first challenge here was that I needed to have two horizontal rows of items in the Scroller. Thanks to a post over at The Problem Solver and the WrapPanel from the Silverlight Toolkit, I was able to pull this off pretty easily. So, I went to work on a custom template for a ScrollBar. My initial thought here was that I’d stretch the ScrollBar to the Height of the ListBox, delete the Thumb, make the Background Transparent, and delete the Large Increase/Decrease controls. This worked great, but the scrolling was what you would typically get when clicking the Small Increase/Decrease portions of a ScrollBar (i.e. the little arrows). What we really needed was for each click to move to the next/previous column of items.
At this point, I decided to lose the custom template for the ScrollBar and just go with a pair of RepeatButtons for the Previous/Next buttons so that I could have a little more control over things. As I didn’t want to see the ScrollBar, I had to hide it by setting the HorizontalScrollBarVisibility and the VerticalScrollBarVisibility to Hidden in the ControlTemplate of the ListBox.
The ScrollViewer (that is part of our ListBox) is what we want to manipulate to give this per-item scroll experience to the user. There are a couple of important properties of the ScrollViewer that we care about. First, the ScrollableWidth which represents the width of the area that can be scrolled. You can envision this as placing all of the columns end-to-end (including those that are scrolled out of sight) and getting the total width. Secondly, the HorizontalOffset which is simply the indicator of the current scroll position (relative to the entire ScrollableWidth). Using these two properties and the ItemWidth, we can compute how far to move the ScrollViewer for each click of our Previous/Next buttons.
Since I needed direct access to the ScrollViewer in my ListBox and its not readily available from the default ListBox, I decided to create a custom control with a ScrollHost property. I saw this little trick used in the ItemContainerGenerator class of the Silverlight Toolkit (which has some really great utility code along with the awesome controls….well worth your time to investigate that). Also, since I was using a WrapPanel as my ItemsPanelTemplate and I needed the ItemWidth for my calculations and since WrapPanel just happens to have an ItemWidth property, I decided to expose that as a property as well.
I overrode the PrepareContainerForItemOverride() method in my custom ScrollerListBox in order to set these properties. This worked pretty well and with a little math and a call to ScrollViewer.ScrollToHorizontalOffset(), I was able to create the scrolling behavior that I needed.
But….wouldn’t it be cool if the scrolling was animated? …it is Silverlight after all and every demo that I’ve ever seen at a conference with Silverlight has animation. This proved to be a little more difficult as you can’t directly animate HorizontalOffset. While searching for a way to solve that, I came across a really great solution on Rob’s Usability Development blog for creating an AnimationHelperControl to give you something to animate. His post also made use of his excellent AnimateTo extension methods that he describes in another post.
As I didn’t want to hard code the amount of time it took to animate the scroll, I added a ScrollTime Dependency Property to the ScrollerControl and was able to set that declaratively in my Page.Xaml.
I think the final result gives a nice subtle use of animation that doesn’t distract the user from their data. (As an aside…for a REALLY nice usage of animation in a Silverlight application, check out the one that Billy Hollis shows in this video on dnrTV).
My demo uses employees that I’ve generated in a TestData class and then binds the various fields to the ItemTemplate of my ListBox using standard Silverlight DataBinding. I was a bit too lazy to create a separate image for each of my employees, so I used the same image for each. These images could just as easily be streamed from the database, but that’s a different post.
Here is the live demo.
Here is the project source.
I have a few more ideas for the ScrollerControl such as the ability to drag between the items (sort of like the iPhone). Anyway, I’ll post those as I implement them.