public async static Task ScrollToIndex(this ListViewBase listViewBase, int index)
        {
            bool isVirtualizing = default(bool);
            double previousHorizontalOffset = default(double), previousVerticalOffset = default(double);

            // get the ScrollViewer withtin the ListView/GridView
            var scrollViewer = listViewBase.GetScrollViewer();
            // get the SelectorItem to scroll to
            var selectorItem = listViewBase.ContainerFromIndex(index) as SelectorItem;

            // when it's null, means virtualization is on and the item hasn't been realized yet
            if (selectorItem == null)
            {
                isVirtualizing = true;

                previousHorizontalOffset = scrollViewer.HorizontalOffset;
                previousVerticalOffset = scrollViewer.VerticalOffset;

                // call task-based ScrollIntoViewAsync to realize the item
                await listViewBase.ScrollIntoViewAsync(listViewBase.Items[index]);

                // this time the item shouldn't be null again
                selectorItem = (SelectorItem)listViewBase.ContainerFromIndex(index);
            }

            // calculate the position object in order to know how much to scroll to
            var transform = selectorItem.TransformToVisual((UIElement)scrollViewer.Content);
            var position = transform.TransformPoint(new Point(0, 0));

            // when virtualized, scroll back to previous position without animation
            if (isVirtualizing)
            {
                await scrollViewer.ChangeViewAsync(previousHorizontalOffset, previousVerticalOffset, true);
            }

            // scroll to desired position with animation!
            scrollViewer.ChangeView(position.X, position.Y, null);
        }
        /// <summary>
        /// Synchronizes the scroll offset of the target ItemsControl to the offset of the source ItemsControl
        /// such that the first visible item in both controls is the same item.
        /// </summary>
        /// <param name="targetItemsControl">The ItemsControl whose scroll offset is to be updated.</param>
        /// <param name="sourceItemsControl">The ItemsControl whose scroll offset will be matched in the target.</param>
        /// <param name="throwOnFail">Specifies whether an exception should be thrown on failure.</param>
        public static void SynchronizeScrollOffset(this ItemsControl targetItemsControl, ItemsControl sourceItemsControl, bool throwOnFail = false)
        {
            var firstVisibleIndex = sourceItemsControl.GetFirstVisibleIndex();

            if (firstVisibleIndex == -1)
            {
                if (throwOnFail)
                {
                    throw new InvalidOperationException();
                }

                return;
            }

            var targetListBox = targetItemsControl as ListBox;

            if (targetListBox != null)
            {
                targetListBox.ScrollIntoView(sourceItemsControl.IndexFromContainer(sourceItemsControl.ContainerFromIndex(firstVisibleIndex)));
                return;
            }

            var targetListViewBase = targetItemsControl as ListViewBase;

            if (targetListViewBase != null)
            {
                targetListViewBase.ScrollIntoView(sourceItemsControl.IndexFromContainer(sourceItemsControl.ContainerFromIndex(firstVisibleIndex)), ScrollIntoViewAlignment.Leading);
                return;
            }

            var scrollViewer = targetItemsControl.GetScrollViewer();

            if (scrollViewer != null)
            {
                var container = (FrameworkElement)targetItemsControl.ContainerFromIndex(firstVisibleIndex);
                var position = container.TransformToVisual(scrollViewer).TransformPoint(new Point());
                scrollViewer.ChangeView(scrollViewer.HorizontalOffset + position.X, scrollViewer.VerticalOffset + position.Y, null);
            }
        }
        public static IList<WeakReference> VisibleItems(this ItemsControl itemsControl)
        {
            var result = new List<WeakReference>();

            if (VisualTreeHelper.GetChildrenCount(itemsControl) == 0) return result;

            var scrollHost = VisualTreeHelper.GetChild(itemsControl, 0) as ScrollViewer;
            if (scrollHost == null) return result;

            if (itemsControl.Items == null) return result;
            itemsControl.UpdateLayout();

            int index;
            for (index = 0; index < itemsControl.Items.Count; index++)
            {
#if NETFX_CORE || WINDOWS_81_PORTABLE
                var container = (FrameworkElement)itemsControl.ContainerFromIndex(index);
#endif
#if WINDOWS_PHONE
                var container = (FrameworkElement)itemsControl.ItemContainerGenerator.ContainerFromIndex(index);
#endif
                if (container != null)
                {
                    GeneralTransform itemTransform = null;
                    try
                    {
                        itemTransform = container.TransformToVisual(scrollHost);
                    }
                    catch (ArgumentException)   // not always in visual tree
                    {
                        return result;
                    }

#if NETFX_CORE || WINDOWS_81_PORTABLE
                    var boundingBox = new Rect(
                        itemTransform.TransformPoint(new Point()),
                        itemTransform.TransformPoint(new Point(container.ActualWidth, container.ActualHeight)));
#elif WINDOWS_PHONE
                    var boundingBox = new Rect(
                        itemTransform.Transform(new Point()), 
                        itemTransform.Transform(new Point(container.ActualWidth, container.ActualHeight)));
#endif

                    if (boundingBox.Bottom > 0)
                    {
                        result.Add(new WeakReference(container));
                        index++;
                        break;
                    }
                }
            }

            for (; index < itemsControl.Items.Count; index++)
            {
#if NETFX_CORE || WINDOWS_81_PORTABLE
                var container = (FrameworkElement)itemsControl.ContainerFromIndex(index);
#endif
#if WINDOWS_PHONE
                var container = (FrameworkElement)itemsControl.ItemContainerGenerator.ContainerFromIndex(index);
#endif
                GeneralTransform itemTransform = null;
                try
                {
                    itemTransform = container.TransformToVisual(scrollHost);
                }
                catch (ArgumentException)   // not always in visual tree
                {
                    return result;
                }

#if NETFX_CORE || WINDOWS_81_PORTABLE
                var boundingBox = new Rect(
                    itemTransform.TransformPoint(new Point()),
                    itemTransform.TransformPoint(new Point(container.ActualWidth, container.ActualHeight)));
#elif WINDOWS_PHONE
                var boundingBox = new Rect(
                    itemTransform.Transform(new Point()), 
                    itemTransform.Transform(new Point(container.ActualWidth, container.ActualHeight)));
#endif

                if (boundingBox.Top >= scrollHost.ActualHeight)
                    break;

                result.Add(new WeakReference(container));
            }

            return result;
        }
        public static async Task AnimateItems( this ItemsControl itemsControl,  AnimationDefinition animationDefinition,  double itemDelay = 0.05)
        {
            if (itemsControl == null) return;
            if (animationDefinition == null) return;
            if (itemDelay <= 0.0)
                itemDelay = 0.05;

            itemsControl.UpdateLayout();

            var animations = new List<Task>();

            double baseDelay = animationDefinition.Delay;

            for (int index = 0; index < itemsControl.Items.Count; index++)
            {
#if NETFX_CORE || WINDOWS_81_PORTABLE
                var container = (FrameworkElement)itemsControl.ContainerFromIndex(index);
#endif
#if WINDOWS_PHONE
                var container = (FrameworkElement) itemsControl.ItemContainerGenerator.ContainerFromIndex(index);
#endif
                if (container == null) continue;

                animationDefinition.Delay = baseDelay + (itemDelay*index);
                AnimationManager.ClearAnimationProperties(container);
                animations.Add(
                    container.AnimateAsync(animationDefinition));
            }

            animationDefinition.Delay = baseDelay;

            await Task.WhenAll(animations.ToArray());
        }
        public async static Task ScrollToIndex(this ListViewBase listViewBase, int index)
        {
            bool isVerticalScrolling, isVirtualizing = default(bool);
            double previousOffset = default(double);

            // if it's a ListView, then we assume the scrolling is vertical
            if (listViewBase is ListView)
            {
                isVerticalScrolling = true;
            }
            // if it's a GridView, then we assume the scrolling is horizontal
            else if (listViewBase is GridView)
            {
                isVerticalScrolling = false;
            }
            else
            {
                throw new ArgumentException("The control needs to inherit from ListViewBase!");
            }

            // get the ScrollViewer withtin the ListView/GridView
            var scrollViewer = listViewBase.GetScrollViewer();
            // get the SelectorItem to scroll to
            var selectorItem = listViewBase.ContainerFromIndex(index) as SelectorItem;

            // when it's null, means virtualization is on and the item hasn't been realized yet
            if (selectorItem == null)
            {
                isVirtualizing = true;
                previousOffset = isVerticalScrolling ? scrollViewer.VerticalOffset : scrollViewer.HorizontalOffset;

                // call ScrollIntoView to realize the item
                listViewBase.ScrollIntoView(listViewBase.Items[index], ScrollIntoViewAlignment.Leading);
                await Task.Delay(1);
                selectorItem = (SelectorItem)listViewBase.ContainerFromIndex(index);
            }

            // calculate the position object in order to know how much to scroll to
            var transform = selectorItem.TransformToVisual((UIElement)scrollViewer.Content);
            var position = transform.TransformPoint(new Point(0, 0));

            // when virtualized, scroll backward/forward a little bit (listViewBase.ActualHeight * 2) to allow animation to be at least visible
            if (isVirtualizing)
            {
                if (isVerticalScrolling)
                {
                    scrollViewer.ChangeView(null, position.Y + (position.Y > previousOffset ? -1 : 1) * listViewBase.ActualHeight * 2, null, true);
                }
                else
                {
                    scrollViewer.ChangeView(position.X + (position.X > previousOffset ? -1 : 1) * listViewBase.ActualWidth * 2, null, null, true);
                }
            }

            // do the scrolling
            if (isVerticalScrolling)
            {
                scrollViewer.ChangeView(null, position.Y, null);
            }
            else
            {
                scrollViewer.ChangeView(position.X, null, null);
            }
        }