An MVVM marquee control (in Silverlight and WPF)

Marquee controls have been around for a long time and have been abused for much of that time.  Their place in the user experience should really be very small, since they have a number of drawbacks – information isn’t static for the user to absorb, they typically only display a limited amount of information and they assume a certain reading speed (which may be too slow or too fast).  Nevertheless, there are times when marquee controls are useful, news or stock tickers for example.

So I needed a marquee to display a news ticker in Silverlight, and I didn’t want to have use any nasty code-behind.  Nothing seemed entirely appropriate, so I used the marquee control from Jobi Joy as a starting point and extended it:

  1. The control needed to work in an MVVM application.  This meant that it mustn't rely on code calling “ticker.StartAnimation()” or anything like that – it had to all work by binding to a view model.
  2. I changed the animation so that there is always as much content visible as possible, i.e. the first bit of content starts at the left-hand side, not at the right-hand side
  3. Just for kicks, I added a little bit of acceleration to the scrolling, so that it is not always a constant speed
  4. I got the marquee working in WPF as well as Silverlight

Some friends contributed here too – Marcin extracted the control template into generic.xaml, and Marlon fixed an issue where the marquee would zoom uncomfortably fast if there was too much content.  The result is that you can just use a few lines of XAML to achieve the effect:

<Controls:Marquee InitialPause="2"
                  FinalPause="2"
                  Duration="5"
                  Acceleration="0"
                  Height="20"
                  Grid.Row="0">
    <ItemsControl ItemsSource="{Binding Headlines}" ItemTemplate="{StaticResource HeadlineTemplate}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Controls:Marquee>

The control template for this uses a ScrollViewer to house a Canvas.  The content to be scrolled is placed on the canvas.

<ScrollViewer Width="Auto"
              Height="Auto"
              BorderThickness="0"
              HorizontalContentAlignment="Stretch"
              VerticalContentAlignment="Stretch"
              VerticalScrollBarVisibility="Disabled"
              Margin="0"
              Padding="0">
    <Canvas>
        <ContentPresenter x:Name="PART_Content"
                          VerticalAlignment="Center"
                          HorizontalAlignment="Center" />
    </Canvas>
</ScrollViewer>

An animation gets calculated and setup to scroll the content from right to left:

Storyboard _storyboard = new Storyboard();
DoubleAnimationUsingKeyFrames _animation = new DoubleAnimationUsingKeyFrames();

double totalDuration = InitialPause + Duration * _contentPart.ActualWidth / ActualWidth;
_animation.Duration = new Duration(TimeSpan.FromSeconds(totalDuration));

// _animation.KeyFrames setup omitted here for clarity. Get the source code if you want it.

_storyboard.Duration = new Duration(TimeSpan.FromSeconds(value + FinalPause));

if (_storyboard.Children.Count == 0)
{
    _storyboard.Children.Add(_animation);
    Storyboard.SetTargetProperty(_animation, new PropertyPath("(Canvas.Left)"));
    Storyboard.SetTarget(_animation, _contentPart);

    _storyboard.RepeatBehavior = RepeatBehavior.Forever;
}

_storyboard.Begin();

The result should show up below, if you have Silverlight 4 installed on your machine.

The full source code and samples in WPF and Silverlight can be found in the SharpFellows Toolkit.  And just for practice I used the Reactive Framework in the sample to asynchronously download and display some news headlines from a website.  Enjoy!

blog comments powered by Disqus