Introduction
Even though we have a bunch of APIs available for Windows phone scrollviewer, we will be left alone when it comes to controlling the scroll speed of scrollviewer component.
Background
The behavior which forced myself and left me alone is a simple use case. I had a list of images which was arranged vertically inside a scrollviewer. I have to provide an interface which allows the user not scrolling more than one image at a time, either upwards or downwards. So there were options to move to a desired offset position inside the scrollviewer, but the thing which made me worry was how to decelerate the speed of scrolling. We do, .NET developers have options for this scenario when our developing platform is winRT / WPF, for Windows phone developers we do not have that. Initially, I went with some approaches like traversing through the visual tree of scrollviewer to find its underlying Scrollbar component and handling the speed using the Scrollbar, I failed in that case also. And finally what I have found is a simple one I would suggest for developers also who want to solve a scenario like this one.
Using the Code
What you need actually is a few lines of code to develop your own customized vertical panel. You can modify the code in the same way to resolve the scenario if your case is a horizontal swapping of elements.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace CustomLibrary
{
public class VerticalRepeater : Grid
{
private ScrollViewer rootScrollElement;
private StackPanel parentPanel;
private Grid overlayElement;
double previousOffset = 0.0;
DateTime lastoccuredTime = DateTime.MinValue;
static Duration _scrollDuration = new Duration(new TimeSpan(10000000));
const int SCROLLINTERVAL = 300;
public List<UIElement> Items
{
get { return (List<UIElement>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public double VerticalSliderValue
{
get { return (double)GetValue(VerticalSliderValueProperty); }
set { SetValue(VerticalSliderValueProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items",
typeof(List<UIElement>), typeof(VerticalRepeater), new PropertyMetadata(null));
public static readonly DependencyProperty VerticalSliderValueProperty =
DependencyProperty.Register("VerticalSliderValue",
typeof(double), typeof(VerticalRepeater),
new PropertyMetadata(0.0, OnVerticalSliderValueChanged));
public Duration ScrollDuration
{
get { return (Duration)GetValue(ScrollDurationProperty); }
set { SetValue(ScrollDurationProperty, value); }
}
public static readonly DependencyProperty ScrollDurationProperty =
DependencyProperty.Register("ScrollDuration",
typeof(Duration), typeof(VerticalRepeater), new PropertyMetadata(_scrollDuration));
private static void OnVerticalSliderValueChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
VerticalRepeater sender = d as VerticalRepeater;
if (sender != null)
{
double _newvalue = (double)e.NewValue;
sender.rootScrollElement.ScrollToVerticalOffset(_newvalue);
}
}
public VerticalRepeater()
{
Items = new List<UIElement>();
parentPanel = new StackPanel();
rootScrollElement = new ScrollViewer();
overlayElement = new Grid()
{ Background = new SolidColorBrush(Colors.Transparent), Height = Height, Width = Width };
overlayElement.ManipulationDelta += VerticalRepeater_ManipulationDelta;
}
private void VerticalRepeater_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
Debug.WriteLine("previousOffset " + previousOffset);
Debug.WriteLine("VerticalOffset " + rootScrollElement.VerticalOffset);
bool isvalid = (DateTime.Now.Subtract(lastoccuredTime).Milliseconds > SCROLLINTERVAL);
Debug.WriteLine("condition: " + isvalid);
Debug.WriteLine("--------------------------------------");
var _isscrolledUp = e.DeltaManipulation.Translation.Y < 0;
if (lastoccuredTime == DateTime.MinValue || isvalid)
{
if (previousOffset <= rootScrollElement.VerticalOffset || previousOffset == 0.0)
{
if (previousOffset == rootScrollElement.ScrollableHeight)
if (_isscrolledUp)
return;
else
rootScrollElement.ScrollToVerticalOffset(previousOffset - Height);
if (_isscrolledUp)
{
var startHeight = previousOffset;
var newheight = previousOffset + Height;
if (newheight <= rootScrollElement.ScrollableHeight)
{
rootScrollElement.ScrollToVerticalOffset(newheight);
DoAnimation(startHeight, newheight);
}
}
else
{
var newheight = previousOffset - Height;
if (newheight >= 0.0)
{
rootScrollElement.ScrollToVerticalOffset(newheight);
DoAnimation(previousOffset, newheight);
}
}
}
previousOffset = rootScrollElement.VerticalOffset;
lastoccuredTime = DateTime.Now;
}
}
private void DoAnimation(double startHeight, double newheight)
{
double animHeight = newheight;
Storyboard sb = new Storyboard();
DoubleAnimation animation =
new DoubleAnimation { From = startHeight, To = animHeight, Duration = ScrollDuration };
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation,
new PropertyPath("VerticalSliderValue"));
animation.EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseInOut
};
sb.Children.Add(animation);
sb.Begin();
}
public void AddChildElementsToList()
{
rootScrollElement.Height = this.Height;
foreach (var item in this.Items)
{
parentPanel.Children.Add(item);
}
rootScrollElement.Content = parentPanel;
rootScrollElement.IsHitTestVisible = false;
this.Children.Add(rootScrollElement);
this.Children.Add(overlayElement);
}
~VerticalRepeater()
{
overlayElement.ManipulationDelta -= VerticalRepeater_ManipulationDelta;
parentPanel = null;
rootScrollElement = null;
overlayElement = null;
}
}
}
VerticalRepeater repeater = new VerticalRepeater() { Height = 300 ,Width=300};
Border bdr = new Border() { Height = 300, Width = 300,
Background = new SolidColorBrush(Colors.Red) };
repeater.Items.Add(bdr);
bdr = new Border() { Height = 300, Width = 300,
Background = new SolidColorBrush(Colors.Blue) };
repeater.Items.Add(bdr);
bdr = new Border() { Height = 300, Width = 300,
Background = new SolidColorBrush(Colors.Green) };
repeater.Items.Add(bdr);
bdr = new Border() { Height = 300, Width = 300,
Background = new SolidColorBrush(Colors.Yellow) };
repeater.Items.Add(bdr);
bdr = new Border() { Height = 300, Width = 300,
Background = new SolidColorBrush(Colors.Orange) };
repeater.Items.Add(bdr);
repeater.AddChildElementsToList();
History
- Updated the code on Feb 5th, 2015