Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Windows-Phone-7

Controlling the Speed of Scroll Viewer in Windows Phone 8

4.69/5 (4 votes)
5 Feb 2015CPOL1 min read 11.7K  
To control the speed of scroll viewer component in Windows phone

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.

C#
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
{
    /// <summary>
    /// Provides a lightweight control to scroll over a vertical list with controlled speed.
    /// </summary>
    public class VerticalRepeater : Grid
    {
        private ScrollViewer rootScrollElement;

        private StackPanel parentPanel;

        private Grid overlayElement; // overlay added over the scrollviewer to initiate scrolling.

        double previousOffset = 0.0;

        DateTime lastoccuredTime = DateTime.MinValue;

        static Duration _scrollDuration = new Duration(new TimeSpan(10000000));

        const int SCROLLINTERVAL = 300;

        /// <summary>
        /// Collection of UIElement needs to be added inside the scrolling component.
        /// </summary>
        public List<UIElement> Items
        {
            get { return (List<UIElement>)GetValue(ItemsProperty); }
            set { SetValue(ItemsProperty, value); }
        }
       
        /// <summary>
        /// Supports animation of scrollviewer component.
        /// </summary>
        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));

        /// <summary>
        /// Property to control the speed of scrolling time to the next component.
        /// </summary>
        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);
            }
        }

        /// <summary>
        /// Initializes the component.
        /// </summary>
        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;
            }
        }

        /// <summary>
        /// Performs the animation action on scrollviewer scroll.
        /// </summary>
        /// <param name="startHeight"></param>
        /// <param name="newheight"></param>
        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;
        }
    }
}

            ///Sample implementation of the vertical repeater component .

            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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)