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 = ItemMargin; wrapper.Click += OnWrapperClick; } wrapper.DataContext = wrapper.Content = content; parent.Children.Add(wrapper); // Need to do this before calling ApplyTemplate if (!reuse) { wrapper.ApplyTemplate(); wrapper.SetState(LoopingSelectorItem.State.Normal, false); } // Item should be visible if this control is expanded. if (IsExpanded) { // Uses transitions only when going from normal to expanded state. wrapper.SetState(LoopingSelectorItem.State.Expanded, !_actualIsExpanded); } return(wrapper); }
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e) { if (_isDragging) { AnimatePanel(_panDuration, _panEase, _dragTarget += e.DeltaManipulation.Translation.Y); #if WP8 _changeStateAfterAnimation = false; #endif 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(); } }
private int GetClosestItem() { if (!IsReady) { return(-1); } double actualItemHeight = ActualItemHeight; int count = _itemsPanel.Children.Count; double panelY = _panningTransform.Y; double halfHeight = actualItemHeight / 2; int found = -1; double closestDistance = double.MaxValue; for (int index = 0; index < count; ++index) { LoopingSelectorItem wrapper = (LoopingSelectorItem)_itemsPanel.Children[index]; double distance = Math.Abs((wrapper.Transform.Y + halfHeight) + panelY); if (distance <= halfHeight) { found = index; break; } else if (closestDistance > distance) { closestDistance = distance; found = index; } } return(found); }
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 = ItemMargin; wrapper.Click += new EventHandler <EventArgs>(wrapper_Click); } wrapper.DataContext = wrapper.Content = content; parent.Children.Add(wrapper); // Need to do this before calling ApplyTemplate if (!reuse) { wrapper.ApplyTemplate(); } return(wrapper); }
void listener_Flick(object sender, FlickGestureEventArgs e) { //Debug.WriteLine("listener_Flick"); if (e.Direction == Orientation.Vertical) { _state = State.Flicking; _selectedItem = null; if (!IsExpanded) { IsExpanded = true; } Point velocity = new Point(0, e.VerticalVelocity); double flickDuration = PhysicsConstants.GetStopTime(velocity); Point flickEndPoint = PhysicsConstants.GetStopPoint(velocity); IEasingFunction flickEase = PhysicsConstants.GetEasingFunction(flickDuration); AnimatePanel(new Duration(TimeSpan.FromSeconds(flickDuration)), flickEase, _panningTransform.Y + flickEndPoint.Y); e.Handled = true; _selectedItem = null; UpdateItemState(); } }
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); }
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; }
void listener_DragStarted(object sender, DragStartedGestureEventArgs e) { //Debug.WriteLine("listener_DragStarted"); if (e.Direction == Orientation.Vertical) { _state = State.Dragging; e.Handled = true; _selectedItem = null; if (!IsExpanded) { IsExpanded = true; } _dragTarget = _panningTransform.Y; 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 = new Point(0, e.FinalVelocities.LinearVelocity.Y); double flickDuration = PhysicsConstants.GetStopTime(velocity); Point flickEndPoint = PhysicsConstants.GetStopPoint(velocity); IEasingFunction flickEase = PhysicsConstants.GetEasingFunction(flickDuration); double endPoint = _panningTransform.Y + flickEndPoint.Y; double adjustedEndPoint = Math.Round(endPoint / ActualItemHeight) * ActualItemHeight; flickDuration *= adjustedEndPoint / endPoint; AnimatePanel(new Duration(TimeSpan.FromSeconds(flickDuration)), flickEase, adjustedEndPoint); #if WP8 _changeStateAfterAnimation = false; #endif 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 != item) { _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; }); } #if WP7 _selectedItem.SetState(LoopingSelectorItem.State.Selected, true); #endif TranslateTransform transform = item.Transform; if (transform != null) { double newPosition = -transform.Y - Math.Round(item.ActualHeight / 2); if (_panningTransform.Y != newPosition) { AnimatePanel(_selectDuration, _selectEase, newPosition); #if WP8 _changeStateAfterAnimation = true; #endif } else { _selectedItem.SetState(LoopingSelectorItem.State.Selected, true); } } }
private void SelectAndSnapToClosest() { if (!IsReady) { return; } int index = GetClosestItem(); if (index == -1) { return; } LoopingSelectorItem item = (LoopingSelectorItem)_itemsPanel.Children[index]; SelectAndSnapTo(item); }
private void SelectAndSnapToClosest() { if (!IsReady) { return; } int index = GetClosestItem(); if (index == -1) { return; } LoopingSelectorItem item = (LoopingSelectorItem)_itemsPanel.Children[index]; //Debug.WriteLine("Selecting from SelectAndSnapToClosest {0}", item.DataContext); SelectAndSnapTo(item); }
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(0, e.FinalVelocities.LinearVelocity.Y); double flickDuration = PhysicsConstants.GetStopTime(velocity); Point flickEndPoint = PhysicsConstants.GetStopPoint(velocity); IEasingFunction flickEase = PhysicsConstants.GetEasingFunction(flickDuration); AnimatePanel(new Duration(TimeSpan.FromSeconds(flickDuration)), flickEase, _panningTransform.Y + flickEndPoint.Y); e.Handled = true; _selectedItem = null; UpdateItemState(); } if (_state == State.Dragging) { SelectAndSnapToClosest(); } _state = State.Expanded; } }
/// <summary> /// Balances the items. /// </summary> private void Balance() { if (!IsReady) { return; } double actualItemWidth = ActualItemWidth; double actualItemHeight = ActualItemHeight; _additionalItemsCount = (int)Math.Round((ActualHeight * 1.5) / actualItemHeight); 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); closestToMiddle.Transform.Y = -actualItemHeight / 2; closestToMiddle.Transform.X = (ActualWidth - actualItemWidth) / 2; closestToMiddle.SetState(LoopingSelectorItem.State.Selected, false); } else { closestToMiddleIndex = GetClosestItem(); closestToMiddle = (LoopingSelectorItem)_itemsPanel.Children[closestToMiddleIndex]; } if (IsExpanded) { 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. _maximumPanelScroll = -firstItem.Transform.Y - actualItemHeight / 2; if (_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); newItem.Transform.X = (ActualWidth - actualItemWidth) / 2; } // Put the new item on the top newItem.Transform.Y = firstItem.Transform.Y - actualItemHeight; 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. _minimumPanelScroll = -lastItem.Transform.Y - actualItemHeight / 2; if (_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); newItem.Transform.X = (ActualWidth - actualItemWidth) / 2; } // Put the new item on the bottom newItem.Transform.Y = lastItem.Transform.Y + actualItemHeight; newItem.InsertAfter(lastItem); lastItem = newItem; ++itemsAfterCount; } } _temporaryItemsPool = null; } }
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e) { 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(); } }
private static LoopingSelectorItem GetLastItem(LoopingSelectorItem item, out int count) { count = 0; while (item.Next != null) { ++count; item = item.Next; } return item; }
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) { double newPosition = -transform.Y - Math.Round(item.ActualHeight / 2); if (_panningTransform.Y != 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((ActualHeight * 1.5) / actualItemHeight); 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); closestToMiddle.Transform.Y = -actualItemHeight / 2; closestToMiddle.Transform.X = (ActualWidth - actualItemWidth) / 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. _maximumPanelScroll = - firstItem.Transform.Y - actualItemHeight / 2; if (_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); newItem.Transform.X = (ActualWidth - actualItemWidth) / 2; } // Put the new item on the top newItem.Transform.Y = firstItem.Transform.Y - actualItemHeight; 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. _minimumPanelScroll = - lastItem.Transform.Y - actualItemHeight / 2; if (_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); newItem.Transform.X = (ActualWidth - actualItemWidth) / 2; } // Put the new item on the bottom newItem.Transform.Y = lastItem.Transform.Y + actualItemHeight; 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; }