예제 #1
0
        private LoopingSelectorItem CreateAndAddItem(Panel parent, object content)
        {
            bool reuse = _temporaryItemsPool != null && _temporaryItemsPool.Count > 0;

            LoopingSelectorItem wrapper = reuse ? _temporaryItemsPool.Dequeue() : new LoopingSelectorItem();

            if (!reuse)
            {
                wrapper.ContentTemplate = this.ItemTemplate;
                wrapper.Width           = ItemSize.Width;
                wrapper.Height          = ItemSize.Height;

                wrapper.Padding = ItemPadding;

                wrapper.Click += OnWrapperClick;
            }

            wrapper.DataContext = wrapper.Content = content;

            parent.Children.Add(wrapper);             // Need to do this before calling ApplyTemplate
            if (!reuse)
            {
                wrapper.ApplyTemplate();
            }

            return(wrapper);
        }
예제 #2
0
        private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            if (Orientation == Orientation.Vertical)
            {
                if (_isDragging)
                {
                    AnimatePanel(_panDuration, _panEase, _dragTarget += e.DeltaManipulation.Translation.Y);
                    e.Handled = true;
                }
                else if (Math.Abs(e.CumulativeManipulation.Translation.X) > DragSensitivity)
                {
                    _isAllowedToDragVertically = false;
                }
                else if (_isAllowedToDragVertically && Math.Abs(e.CumulativeManipulation.Translation.Y) > DragSensitivity)
                {
                    _isDragging   = true;
                    _state        = State.Dragging;
                    e.Handled     = true;
                    _selectedItem = null;

                    if (!IsExpanded)
                    {
                        IsExpanded = true;
                    }

                    _dragTarget = _panningTransform.Y;
                    UpdateItemState();
                }
            }
            else
            {
                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();
                }
            }
        }
예제 #3
0
        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;

                    if (Orientation == Orientation.Vertical)
                    {
                        velocity = new Point(0, e.FinalVelocities.LinearVelocity.Y);
                    }
                    else
                    {
                        velocity = new Point(e.FinalVelocities.LinearVelocity.X, 0);
                    }

                    double          flickDuration = PhysicsConstants.GetStopTime(velocity, Friction, MaximumSpeed, ParkingSpeed);
                    Point           flickEndPoint = PhysicsConstants.GetStopPoint(velocity, Friction, MaximumSpeed, ParkingSpeed);
                    IEasingFunction flickEase     = PhysicsConstants.GetEasingFunction(flickDuration, Friction);

                    double to;
                    if (Orientation == Orientation.Vertical)
                    {
                        to = _panningTransform.Y + flickEndPoint.Y;
                    }
                    else
                    {
                        to = _panningTransform.X + flickEndPoint.X;
                    }

                    AnimatePanel(new Duration(TimeSpan.FromSeconds(flickDuration)), flickEase, to);

                    e.Handled = true;

                    _selectedItem = null;
                    UpdateItemState();
                }

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

                _state = State.Expanded;
            }
        }
예제 #4
0
        private static LoopingSelectorItem GetFirstItem(LoopingSelectorItem item, out int count)
        {
            count = 0;
            while (item.Previous != null)
            {
                ++count;
                item = item.Previous;
            }

            return(item);
        }
예제 #5
0
        private static LoopingSelectorItem GetLastItem(LoopingSelectorItem item, out int count)
        {
            count = 0;
            while (item.Next != null)
            {
                ++count;
                item = item.Next;
            }

            return(item);
        }
예제 #6
0
        private int GetClosestItem()
        {
            if (!IsReady)
            {
                return(-1);
            }

            int count = _itemsPanel.Children.Count;

            double panelY = _panningTransform.Y;
            double panelX = _panningTransform.X;

            double halfHeight = ActualItemHeight / 2;
            double halfWidth  = ActualItemWidth / 2;

            int    found           = -1;
            double closestDistance = double.MaxValue;

            for (int index = 0; index < count; ++index)
            {
                LoopingSelectorItem wrapper = (LoopingSelectorItem)_itemsPanel.Children[index];

                double distance;

                if (Orientation == Orientation.Vertical)
                {
                    distance = Math.Abs((wrapper.Transform.Y + halfHeight) + panelY);

                    if (distance <= halfHeight)
                    {
                        found = index;
                        break;
                    }
                }
                else
                {
                    distance = Math.Abs((wrapper.Transform.X + halfWidth) + panelX);

                    if (distance <= halfWidth)
                    {
                        found = index;
                        break;
                    }
                }

                if (closestDistance > distance)
                {
                    closestDistance = distance;
                    found           = index;
                }
            }

            return(found);
        }
        internal void InsertBefore(LoopingSelectorItem before)
        {
            Next     = before;
            Previous = before.Previous;

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

            before.Previous = this;
        }
 internal void Remove()
 {
     if (Previous != null)
     {
         Previous.Next = Next;
     }
     if (Next != null)
     {
         Next.Previous = Previous;
     }
     Next = Previous = null;
 }
        internal void InsertAfter(LoopingSelectorItem after)
        {
            Next     = after.Next;
            Previous = after;

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

            after.Next = this;
        }
예제 #10
0
        private void SelectAndSnapTo(LoopingSelectorItem item)
        {
            if (item == null)
            {
                return;
            }

            if (_selectedItem != null)
            {
                _selectedItem.SetState(IsExpanded ? LoopingSelectorItem.State.Expanded : LoopingSelectorItem.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(LoopingSelectorItem.State.Selected, true);

            TranslateTransform transform = item.Transform;

            if (transform != null)
            {
                if (Orientation == Orientation.Vertical)
                {
                    double newPosition = -transform.Y - Math.Round(item.ActualHeight / 2);
                    if (_panningTransform.Y != newPosition)
                    {
                        AnimatePanel(_selectDuration, _selectEase, newPosition);
                    }
                }
                else
                {
                    double newPosition = -transform.X - Math.Round(item.ActualWidth / 2);
                    if (_panningTransform.X != newPosition)
                    {
                        AnimatePanel(_selectDuration, _selectEase, newPosition);
                    }
                }
            }
        }
예제 #11
0
        private void SelectAndSnapToClosest()
        {
            if (!IsReady)
            {
                return;
            }

            int index = GetClosestItem();

            if (index == -1)
            {
                return;
            }

            LoopingSelectorItem item = (LoopingSelectorItem)_itemsPanel.Children[index];

            SelectAndSnapTo(item);
        }
        internal void InsertBefore(LoopingSelectorItem before)
        {
            Next = before;
            Previous = before.Previous;

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

            before.Previous = this;
        }
        internal void InsertAfter(LoopingSelectorItem 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 OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
		{
			if (Orientation == Orientation.Vertical)
			{
				if (_isDragging)
				{
					AnimatePanel(_panDuration, _panEase, _dragTarget += e.DeltaManipulation.Translation.Y);
					e.Handled = true;
				}
				else if (Math.Abs(e.CumulativeManipulation.Translation.X) > DragSensitivity)
				{
					_isAllowedToDragVertically = false;
				}
				else if (_isAllowedToDragVertically && Math.Abs(e.CumulativeManipulation.Translation.Y) > DragSensitivity)
				{
					_isDragging = true;
					_state = State.Dragging;
					e.Handled = true;
					_selectedItem = null;

					if (!IsExpanded)
					{
						IsExpanded = true;
					}

					_dragTarget = _panningTransform.Y;
					UpdateItemState();
				}
			}
			else
			{
				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();
				}
			}
		}
		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;

					if (Orientation == Orientation.Vertical)
						velocity = new Point(0, e.FinalVelocities.LinearVelocity.Y);
					else
						velocity = new Point(e.FinalVelocities.LinearVelocity.X, 0);

					double flickDuration = PhysicsConstants.GetStopTime(velocity, Friction, MaximumSpeed, ParkingSpeed);
					Point flickEndPoint = PhysicsConstants.GetStopPoint(velocity, Friction, MaximumSpeed, ParkingSpeed);
					IEasingFunction flickEase = PhysicsConstants.GetEasingFunction(flickDuration, Friction);

					double to;
					if (Orientation == Orientation.Vertical)
						to = _panningTransform.Y + flickEndPoint.Y;
					else
						to = _panningTransform.X + flickEndPoint.X;

					AnimatePanel(new Duration(TimeSpan.FromSeconds(flickDuration)), flickEase, to);

					e.Handled = true;

					_selectedItem = null;
					UpdateItemState();
				}

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

				_state = State.Expanded;
			}
		}
		private void SelectAndSnapTo(LoopingSelectorItem item)
		{
			if (item == null)
			{
				return;
			}

			if (_selectedItem != null)
			{
				_selectedItem.SetState(IsExpanded ? LoopingSelectorItem.State.Expanded : LoopingSelectorItem.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(LoopingSelectorItem.State.Selected, true);

			TranslateTransform transform = item.Transform;
			if (transform != null)
			{
				if (Orientation == Orientation.Vertical)
				{
					double newPosition = -transform.Y - Math.Round(item.ActualHeight / 2);
					if (_panningTransform.Y != newPosition)
					{
						AnimatePanel(_selectDuration, _selectEase, newPosition);
					}
				}
				else
				{
					double newPosition = -transform.X - Math.Round(item.ActualWidth / 2);
					if (_panningTransform.X != newPosition)
					{
						AnimatePanel(_selectDuration, _selectEase, newPosition);
					}
				}
			}
		}
예제 #18
0
        /// <summary>
        /// Balances the items.
        /// </summary>
        private void Balance()
        {
            if (!IsReady)
            {
                return;
            }

            double actualItemWidth  = ActualItemWidth;
            double actualItemHeight = ActualItemHeight;

            if (Orientation == Orientation.Vertical)
            {
                _additionalItemsCount = (int)Math.Round((ActualHeight * 1.5) / actualItemHeight);
            }
            else
            {
                _additionalItemsCount = (int)Math.Round((ActualWidth * 1.5) / actualItemWidth);
            }

            LoopingSelectorItem 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);

                if (Orientation == Orientation.Vertical)
                {
                    closestToMiddle.Transform.Y = -actualItemHeight / 2;
                    closestToMiddle.Transform.X = (ActualWidth - actualItemWidth) / 2;
                }
                else
                {
                    closestToMiddle.Transform.X = -actualItemWidth / 2;
                    closestToMiddle.Transform.Y = (ActualHeight - actualItemHeight) / 2;
                }

                closestToMiddle.SetState(LoopingSelectorItem.State.Selected, false);
            }
            else
            {
                closestToMiddleIndex = GetClosestItem();
                closestToMiddle      = (LoopingSelectorItem)_itemsPanel.Children[closestToMiddleIndex];
            }

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

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

            // Does the top 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.
                        if (Orientation == Orientation.Vertical)
                        {
                            _maximumPanelScroll = -firstItem.Transform.Y - actualItemHeight / 2;
                        }
                        else
                        {
                            _maximumPanelScroll = -firstItem.Transform.X - actualItemWidth / 2;
                        }

                        if (_panelAnimation.To != null && (_isAnimating && _panelAnimation.To.Value > _maximumPanelScroll))
                        {
                            Brake(_maximumPanelScroll);
                        }
                        break;
                    }

                    LoopingSelectorItem newItem = null;

                    // Can an item from the bottom 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);

                        if (Orientation == Orientation.Vertical)
                        {
                            newItem.Transform.X = (ActualWidth - actualItemWidth) / 2;
                        }
                        else
                        {
                            newItem.Transform.Y = (ActualHeight - actualItemHeight) / 2;
                        }
                    }

                    // Put the new item on the top
                    if (Orientation == Orientation.Vertical)
                    {
                        newItem.Transform.Y = firstItem.Transform.Y - actualItemHeight;
                    }
                    else
                    {
                        newItem.Transform.X = firstItem.Transform.X - actualItemWidth;
                    }

                    newItem.InsertBefore(firstItem);
                    firstItem = newItem;

                    ++itemsBeforeCount;
                }
            }

            // Does the bottom 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.
                        if (Orientation == Orientation.Vertical)
                        {
                            _minimumPanelScroll = -lastItem.Transform.Y - actualItemHeight / 2;
                        }
                        else
                        {
                            _minimumPanelScroll = -lastItem.Transform.X - actualItemWidth / 2;
                        }

                        if (_panelAnimation.To != null && (_isAnimating && _panelAnimation.To.Value < _minimumPanelScroll))
                        {
                            Brake(_minimumPanelScroll);
                        }
                        break;
                    }

                    LoopingSelectorItem 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);

                        if (Orientation == Orientation.Vertical)
                        {
                            newItem.Transform.X = (ActualWidth - actualItemWidth) / 2;
                        }
                        else
                        {
                            newItem.Transform.Y = (ActualHeight - ActualItemHeight) / 2;
                        }
                    }

                    // Put the new item on the bottom
                    if (Orientation == Orientation.Vertical)
                    {
                        newItem.Transform.Y = lastItem.Transform.Y + actualItemHeight;
                    }
                    else
                    {
                        newItem.Transform.X = lastItem.Transform.X + actualItemWidth;
                    }

                    newItem.InsertAfter(lastItem);
                    lastItem = newItem;

                    ++itemsAfterCount;
                }
            }

            _temporaryItemsPool = null;
        }
		/// <summary>
		/// Balances the items.
		/// </summary>
		private void Balance()
		{
			if (!IsReady)
			{
				return;
			}

			double actualItemWidth = ActualItemWidth;
			double actualItemHeight = ActualItemHeight;

			if (Orientation == Orientation.Vertical)
				_additionalItemsCount = (int)Math.Round((ActualHeight * 1.5) / actualItemHeight);
			else
				_additionalItemsCount = (int)Math.Round((ActualWidth * 1.5) / actualItemWidth);

			LoopingSelectorItem 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);

				if (Orientation == Orientation.Vertical)
				{
					closestToMiddle.Transform.Y = -actualItemHeight / 2;
					closestToMiddle.Transform.X = (ActualWidth - actualItemWidth) / 2;
				}
				else
				{
					closestToMiddle.Transform.X = -actualItemWidth / 2;
					closestToMiddle.Transform.Y = (ActualHeight - actualItemHeight) / 2;
				}

				closestToMiddle.SetState(LoopingSelectorItem.State.Selected, false);
			}
			else
			{
				closestToMiddleIndex = GetClosestItem();
				closestToMiddle = (LoopingSelectorItem)_itemsPanel.Children[closestToMiddleIndex];
			}

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

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

			// Does the top 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.
						if (Orientation == Orientation.Vertical)
							_maximumPanelScroll = -firstItem.Transform.Y - actualItemHeight / 2;
						else
							_maximumPanelScroll = -firstItem.Transform.X - actualItemWidth / 2;

						if (_panelAnimation.To != null && (_isAnimating && _panelAnimation.To.Value > _maximumPanelScroll))
						{
							Brake(_maximumPanelScroll);
						}
						break;
					}

					LoopingSelectorItem newItem = null;

					// Can an item from the bottom 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);

						if (Orientation == Orientation.Vertical)
							newItem.Transform.X = (ActualWidth - actualItemWidth) / 2;
						else
							newItem.Transform.Y = (ActualHeight - actualItemHeight) / 2;
					}

					// Put the new item on the top
					if (Orientation == Orientation.Vertical)
						newItem.Transform.Y = firstItem.Transform.Y - actualItemHeight;
					else
						newItem.Transform.X = firstItem.Transform.X - actualItemWidth;

					newItem.InsertBefore(firstItem);
					firstItem = newItem;

					++itemsBeforeCount;
				}
			}

			// Does the bottom 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.
						if (Orientation == Orientation.Vertical)
							_minimumPanelScroll = -lastItem.Transform.Y - actualItemHeight / 2;
						else
							_minimumPanelScroll = -lastItem.Transform.X - actualItemWidth / 2;

						if (_panelAnimation.To != null && (_isAnimating && _panelAnimation.To.Value < _minimumPanelScroll))
						{
							Brake(_minimumPanelScroll);
						}
						break;
					}

					LoopingSelectorItem 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);

						if (Orientation == Orientation.Vertical)
							newItem.Transform.X = (ActualWidth - actualItemWidth) / 2;
						else
							newItem.Transform.Y = (ActualHeight - ActualItemHeight) / 2;
					}

					// Put the new item on the bottom
					if (Orientation == Orientation.Vertical)
						newItem.Transform.Y = lastItem.Transform.Y + actualItemHeight;
					else
						newItem.Transform.X = lastItem.Transform.X + actualItemWidth;

					newItem.InsertAfter(lastItem);
					lastItem = newItem;

					++itemsAfterCount;
				}
			}

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

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

			return item;
		}