The Scrollviewer is a great panel and very easy to use. But it has a drawback. It uses a method to set the scrollbars and content position. This means you cannot use a storyboard animation to animate this.
I had 2 projects that required this functionality, and really needed a better solutions then using a DispatcherTimer to create the animation manually. And after a few good nights of sleep (I always have the best ideas after a good nights rest) I came up with what I think is a nice solution.
The idea is that you need a DependencyProperty to animate a Scrollviewer. But since the ScrollViewer is a Sealed control you can’t inherit from it, I needed to wrap a new control around it to make it reusable.
So I took a different approach, I created a AnimationHelperControl, which does not have any visuals but only has a Dependency property and an event that is fired when this property has been changed. In this way you can use it to control anything you want.
Below the source code for the control:
public class AnimationHelperControl : Control
{
public AnimationHelperControl()
{
Visibility = Visibility.Collapsed;
}
public double DoubleValue
{
get { return (double)GetValue(DoubleValueProperty); }
set
{
SetValue(DoubleValueProperty, value);
DoubleValueChanged(value);
}
}
public static readonly DependencyProperty DoubleValueProperty =
DependencyProperty.Register("DoubleValue", typeof(double), typeof(AnimationHelperControl), new PropertyMetadata((sender, e) =>
{
if (e.OldValue != e.NewValue)
{
var ah = (AnimationHelperControl)sender;
ah.DoubleValue = (double)e.NewValue;
}
}));
public event EventHandler<DoubleEventArgs> DoubleValueChange;
private void DoubleValueChanged(double newValue)
{
if (DoubleValueChange != null)
DoubleValueChange(this, new DoubleEventArgs() { Value = newValue });
}
}
public class DoubleEventArgs : EventArgs
{
private double _Value;
public double Value
{
get { return _Value; }
set { _Value = value; }
}
}
Then you add the control, the scrollviewer and 2 buttons to the page.xaml:
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="0.853*"/>
<RowDefinition Height="0.147*"/>
</Grid.RowDefinitions>
<local:AnimationHelperControl x:Name="xScrollViewerHorizontalOffset" Grid.RowSpan="2"/>
<Grid Grid.RowSpan="1" Margin="0,0,0,0">
<ScrollViewer x:Name="xScrollViewer" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden">
<ScrollViewer.Content>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Rectangle Fill="Red" Width="300" Height="200" Margin="20,0,20,0"/>
<Rectangle Fill="Blue" Width="300" Height="200" Margin="0,0,20,0"/>
<Rectangle Fill="Green" Width="300" Height="200" Margin="0,0,20,0"/>
<Rectangle Fill="Purple" Width="300" Height="200" Margin="0,0,20,0"/>
<Rectangle Fill="Black" Width="300" Height="200" Margin="0,0,20,0"/>
<Rectangle Fill="Yellow" Width="300" Height="200" Margin="0,0,20,0"/>
<Rectangle Fill="Cyan" Width="300" Height="200" Margin="0,0,20,0"/>
</StackPanel>
</ScrollViewer.Content>
</ScrollViewer>
</Grid>
<Button HorizontalAlignment="Left" Margin="8,8,0,8" VerticalAlignment="Stretch" Width="65" Content="Left" Grid.Row="1" x:Name="xButtonLeft"/>
<Button HorizontalAlignment="Right" Margin="0,8,8,8" VerticalAlignment="Stretch" Width="66" Content="Right" Grid.Row="1" x:Name="xButtonRight"/>
</Grid>
Remember to the "local" namespace to the xaml.
Then we add the event handler to the page.xaml.cs:
xScrollViewerHorizontalOffset.DoubleValueChange += (sender, e) =>
{
this.xScrollViewer.ScrollToHorizontalOffset(e.Value);
};
Now you could create storyboard animation on the DoubleValue property of the xScrollViewerHorizontalOffset control, but to keep it short and simple I’ll use the AnimateTo extension method of one of my previous posts, which accomplishes the same, only it does it with code. Let’s use the 2 buttons to animate scrolling left and right.
xButtonRight.Click += (sender, e) =>
{
xScrollViewerHorizontalOffset.AnimateDoubleTo(200, "(StoryboardAnimationHelper.DoubleValue)", xScrollViewer.HorizontalOffset + 320);
};
xButtonLeft.Click += (sender, e) =>
{
xScrollViewerHorizontalOffset.AnimateDoubleTo(200, "(StoryboardAnimationHelper.DoubleValue)", xScrollViewer.HorizontalOffset - 320);
};
That should do the trick I’ll try to upload a Demo Project later this weekend.
Stay in the light!
Robertjan Tuit