private static HorizontalLoopingSelectorItem GetFirstItem(HorizontalLoopingSelectorItem item, out int count)
        {
            count = 0;
            while (item.Previous != null)
            {
                ++count;
                item = item.Previous;
            }

            return item;
        }
        private static HorizontalLoopingSelectorItem GetLastItem(HorizontalLoopingSelectorItem item, out int count)
        {
            count = 0;
            while (item.Next != null)
            {
                ++count;
                item = item.Next;
            }

            return item;
        }
        private void SelectAndSnapTo(HorizontalLoopingSelectorItem item)
        {
            if (item == null)
            {
                return;
            }

            if (_selectedItem != null && _selectedItem != item)
            {
                _selectedItem.SetState(IsExpanded ? HorizontalLoopingSelectorItem.State.Expanded : HorizontalLoopingSelectorItem.State.Normal, true);
            }

            if (_selectedItem != item)
            {
                _selectedItem = item;
                // Update DataSource.SelectedItem aynchronously so that animations have a chance to start.
                Dispatcher.BeginInvoke(() =>
                {
                    _isSelecting = true;
                    DataSource.SelectedItem = item.DataContext;
                    _isSelecting = false;
                });

                _selectedItem.SetState(HorizontalLoopingSelectorItem.State.Selected, true);
            }

            TranslateTransform transform = item.Transform;
            if (transform != null)
            {
                double newPosition = -transform.X - Math.Round(item.ActualWidth / 2);
                if (_panningTransform.X != newPosition)
                {
                    AnimatePanel(_selectDuration, _selectEase, newPosition);
                }
            }
        }
        /// <summary>
        /// Balances the items.
        /// </summary>
        private void Balance()
        {
            if (!IsReady)
            {
                return;
            }

            double actualItemWidth = ActualItemWidth;
            double actualItemHeight = ActualItemHeight;

            _additionalItemsCount = (int)Math.Round((ActualWidth * 1.5) / actualItemWidth);

            HorizontalLoopingSelectorItem closestToMiddle = null;
            int closestToMiddleIndex = -1;

            if (_itemsPanel.Children.Count == 0)
            {
                // We need to get the selection and start from there
                closestToMiddleIndex = 0;
                _selectedItem = closestToMiddle = CreateAndAddItem(_itemsPanel, DataSource.SelectedItem);
                closestToMiddle.Transform.X = -actualItemWidth / 2;
                closestToMiddle.Transform.Y = (ActualHeight - actualItemHeight) / 2;
                closestToMiddle.SetState(HorizontalLoopingSelectorItem.State.Selected, false);
            }
            else
            {
                closestToMiddleIndex = GetClosestItem();
                closestToMiddle = (HorizontalLoopingSelectorItem)_itemsPanel.Children[closestToMiddleIndex];
            }

            int itemsBeforeCount;
            HorizontalLoopingSelectorItem firstItem = GetFirstItem(closestToMiddle, out itemsBeforeCount);

            int itemsAfterCount;
            HorizontalLoopingSelectorItem lastItem = GetLastItem(closestToMiddle, out itemsAfterCount);

            // Does the right side need items?
            if (itemsBeforeCount < itemsAfterCount || itemsBeforeCount < _additionalItemsCount)
            {
                while (itemsBeforeCount < _additionalItemsCount)
                {
                    object newData = DataSource.GetPrevious(firstItem.DataContext);
                    if (newData == null)
                    {
                        // There may be room to display more items, but there is no more data.
                        _maximumPanelScroll = -firstItem.Transform.X - actualItemWidth / 2;
                        if (_isAnimating && _panelAnimation.To.Value > _maximumPanelScroll)
                        {
                            Brake(_maximumPanelScroll);
                        }
                        break;
                    }

                    HorizontalLoopingSelectorItem newItem = null;

                    // Can an item from the left side be re-used?
                    if (itemsAfterCount > _additionalItemsCount)
                    {
                        newItem = lastItem;
                        lastItem = lastItem.Previous;
                        newItem.Remove();
                        newItem.Content = newItem.DataContext = newData;
                    }
                    else
                    {
                        // Make a new item
                        newItem = CreateAndAddItem(_itemsPanel, newData);

                        newItem.Transform.Y = (ActualHeight - actualItemHeight) / 2;
                    }

                    // Put the new item on the top

                    //if(newItem.Transform.X < firstItem.Transform.X)
                    newItem.Transform.X = firstItem.Transform.X - actualItemWidth;
                    newItem.InsertBefore(firstItem);
                    firstItem = newItem;

                    ++itemsBeforeCount;
                }
            }

            // Does the left side need items?
            if (itemsAfterCount < itemsBeforeCount || itemsAfterCount < _additionalItemsCount)
            {
                while (itemsAfterCount < _additionalItemsCount)
                {
                    object newData = DataSource.GetNext(lastItem.DataContext);
                    if (newData == null)
                    {
                        // There may be room to display more items, but there is no more data.
                        _minimumPanelScroll = -lastItem.Transform.X - actualItemWidth / 2;
                        if (_isAnimating && _panelAnimation.To.Value < _minimumPanelScroll)
                        {
                            Brake(_minimumPanelScroll);
                        }
                        break;
                    }

                    HorizontalLoopingSelectorItem newItem = null;

                    // Can an item from the top be re-used?
                    if (itemsBeforeCount > _additionalItemsCount)
                    {
                        newItem = firstItem;
                        firstItem = firstItem.Next;
                        newItem.Remove();
                        newItem.Content = newItem.DataContext = newData;
                    }
                    else
                    {
                        // Make a new item
                        newItem = CreateAndAddItem(_itemsPanel, newData);

                        newItem.Transform.Y = (ActualHeight - actualItemHeight) / 2;
                    }

                    // Put the new item on the bottom
                    newItem.Transform.X = lastItem.Transform.X + actualItemWidth;
                    newItem.InsertAfter(lastItem);
                    lastItem = newItem;

                    ++itemsAfterCount;
                }
            }

            _temporaryItemsPool = null;
        }
        void listener_DragStarted(object sender, DragStartedGestureEventArgs e)
        {
            //Debug.WriteLine("listener_DragStarted");

            if (e.Direction == Orientation.Horizontal)
            {
                _state = State.Dragging;
                e.Handled = true;
                _selectedItem = null;
                if (!IsExpanded)
                {
                    IsExpanded = true;
                }
                _dragTarget = _panningTransform.X;
                UpdateItemState();
            }
        }
        void listener_Flick(object sender, FlickGestureEventArgs e)
        {
            //Debug.WriteLine("listener_Flick");

            if (e.Direction == Orientation.Horizontal)
            {
                _state = State.Flicking;

                _selectedItem = null;
                if (!IsExpanded)
                {
                    IsExpanded = true;
                }

                Point velocity = new Point(e.HorizontalVelocity, 0);
                double flickDuration = PhysicsConstants.GetStopTime(velocity);
                Point flickEndPoint = PhysicsConstants.GetStopPoint(velocity);
                IEasingFunction flickEase = PhysicsConstants.GetEasingFunction(flickDuration);

                AnimatePanel(new Duration(TimeSpan.FromSeconds(flickDuration)), flickEase, _panningTransform.X + flickEndPoint.X);

                e.Handled = true;

                _selectedItem = null;
                UpdateItemState();
            }
        }
        internal void InsertBefore(HorizontalLoopingSelectorItem before)
        {
            Next = before;
            Previous = before.Previous;

            if (before.Previous != null)
            {
                before.Previous.Next = this;
            }

            before.Previous = this;
        }
        internal void InsertAfter(HorizontalLoopingSelectorItem after)
        {
            Next = after.Next;
            Previous = after;

            if (after.Next != null)
            {
                after.Next.Previous = this;
            }

            after.Next = this;
        }
 internal void Remove()
 {
     if (Previous != null)
     {
         Previous.Next = Next;
     }
     if (Next != null)
     {
         Next.Previous = Previous;
     }
     Next = Previous = null;
 }
        private void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
        {
            if (_isDragging)
            {
                // See if it was a flick
                if (e.IsInertial)
                {
                    _state = State.Flicking;
                    _selectedItem = null;

                    if (!IsExpanded)
                    {
                        IsExpanded = true;
                    }

                    Point velocity = new Point(e.FinalVelocities.LinearVelocity.X, 0);
                    double flickDuration = PhysicsConstants.GetStopTime(velocity);
                    Point flickEndPoint = PhysicsConstants.GetStopPoint(velocity);
                    IEasingFunction flickEase = PhysicsConstants.GetEasingFunction(flickDuration);

                    AnimatePanel(new Duration(TimeSpan.FromSeconds(flickDuration)), flickEase, _panningTransform.X + flickEndPoint.X);

                    e.Handled = true;

                    _selectedItem = null;
                    UpdateItemState();
                }

                if (_state == State.Dragging)
                {
                    SelectAndSnapToClosest();
                }

                _state = State.Expanded;
            }
        }
        private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            if (_isDragging)
            {
                AnimatePanel(_panDuration, _panEase, _dragTarget += e.DeltaManipulation.Translation.X);
                e.Handled = true;
            }
            else if (Math.Abs(e.CumulativeManipulation.Translation.Y) > DragSensitivity)
            {
                _isAllowedToDragHorizontally = false;
            }
            else if (_isAllowedToDragHorizontally && Math.Abs(e.CumulativeManipulation.Translation.X) > DragSensitivity)
            {
                _isDragging = true;
                _state = State.Dragging;
                e.Handled = true;
                _selectedItem = null;

                if (!IsExpanded)
                {
                    IsExpanded = true;
                }

                _dragTarget = _panningTransform.X;
                UpdateItemState();
            }
        }