internal static void RaiseAutomationEvent(this LoopingListItem loopingListItem) { if (loopingListItem.IsSelected) { loopingListItem.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementAddedToSelection); } }
/// <summary> /// Scrolls to the desired index, starting from the provided visual item. /// </summary> /// <param name="item">The visual item to start from.</param> /// <param name="to">The target index to scroll to.</param> private void ScrollToIndex(LoopingListItem item, int to) { if (!this.IsInitialized()) { return; } int from = item.LogicalIndex; if (item.LogicalIndex == to) { this.BringIntoView(to); return; } int indexOffset = Math.Abs(from - to); int pivotIndex = this.logicalCount / 2; int sign = Math.Sign(from - to); if (indexOffset > pivotIndex) { indexOffset = this.logicalCount - indexOffset; sign *= -1; } double offset = this.visualOffset + (sign * indexOffset * this.itemLength); this.scrollState = LoopingPanelScrollState.ScrollingToIndex; this.AnimateVerticalOffset(offset); }
private double GetSnapOffset(LoopingListItem middleItem) { double middleOffset = this.GetSnapOffsetWhenCentered(); double itemOffset = this.orientation == Orientation.Vertical ? middleItem.VerticalOffset : middleItem.HorizontalOffset; return(this.visualOffset - (itemOffset - middleOffset)); }
private void OnSizeChanged(object sender, SizeChangedEventArgs e) { if (this.owner == null) { return; } this.size = e.NewSize; this.UpdateLayoutParams(); this.UpdateVisualItemCount(); if (this.visualCount != this.Children.Count) { this.UpdateChildren(true); } if (this.isCentered && this.owner != null) { LoopingListItem itemToCenter = this.FindMiddleItem(); if (itemToCenter != null) { this.CenterItem(itemToCenter, true, false); } } }
private void SelectTappedItem(LoopingListItem item) { if (!this.isExpanded) { this.IsExpanded = true; return; } if (item.isHidden || item.IsEmpty) { return; } if (!item.IsSelected) { if (this.isExpanded) { this.SelectItem(item, LoopingListSelectionChangeReason.VisualItemClick); } } else if (this.AutoExpandCollapse) { // Toggle expand/collapse if the tap is over the selected item this.IsExpanded = !this.isExpanded; } if (item.IsSelected && this.itemsPanel != null && this.itemsPanel.IsCentered) { this.itemsPanel.CenterItem(item, false, true); } }
internal void CenterItem(LoopingListItem item, bool select, bool animate) { if (select && this.owner != null) { if (!this.owner.SelectItem(item, LoopingListSelectionChangeReason.VisualItemSnappedToMiddle)) { this.ScrollToIndex(item, this.owner.SelectedIndex, this.owner.SelectedVisualIndex); return; } } if (animate) { double offset = this.GetSnapOffset(item); if (offset != this.visualOffset) { this.ScrollState = LoopingPanelScrollState.SnapScrolling; this.AnimateVerticalOffset(offset); } } else { this.BringIntoView(item.LogicalIndex, item.VisualIndex); } }
private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { LoopingListItem item = d as LoopingListItem; item.isSelected = (bool)e.NewValue; item.UpdateVisualState(false); }
/// <summary> /// Allows to minimize the overhead of creating new instances whenever a logical index changes by formatting an existing data item instance. /// </summary> internal virtual void UpdateDataItem(LoopingListDataItem dataItem, LoopingListItem visualItem) { if (this.ItemsSource != null) { dataItem.Item = this.ItemsSource[visualItem.LogicalIndex]; } this.UpdateItemTemplate(dataItem, visualItem); }
private void CenterMiddleItem(bool select, bool animate) { LoopingListItem middleItem = this.FindMiddleItem(); if (middleItem == null) { return; } this.CenterItem(middleItem, select, animate); }
/// <summary> /// Brings the currently selected index into view and selects the visual item that represents it. /// </summary> internal virtual void SelectCurrentItem() { if (this.itemsPanel != null) { this.itemsPanel.BringIntoView(this.selectedIndex); LoopingListItem item = this.itemsPanel.ItemFromLogicalIndex(this.selectedIndex); if (item != null) { this.SelectItem(item, LoopingListSelectionChangeReason.User); } } }
private void SetDataItem(LoopingListItem visualItem) { LoopingListDataItem dataItem = visualItem.Content as LoopingListDataItem; if (dataItem == null) { dataItem = this.CreateDataItem(visualItem.LogicalIndex); visualItem.DataContext = dataItem; visualItem.Content = dataItem; } this.UpdateDataItem(dataItem, visualItem); }
/// <summary> /// Associates the data at the specified logical index with the provided visual item instance. /// </summary> /// <param name="item">The visual item which data is to be updated.</param> /// <param name="logicalIndex">The logical index that describes the data.</param> /// <param name="visualIndex">The visual index that describes the data.</param> /// <param name="force">True to re-evaluate the data event if the visual item is already associated with the specified logical index.</param> /// <param name="animate">True to update the visual state using transitions.</param> internal void UpdateVisualItem(LoopingListItem item, int logicalIndex, int visualIndex, bool force, bool animate) { if ((!force && item.LogicalIndex == logicalIndex && item.VisualIndex == visualIndex) || item.IsEmpty) { return; } item.LogicalIndex = logicalIndex; item.VisualIndex = visualIndex; this.UpdateItemVisualState(item, new VisualStateUpdateParams(animate, true, true)); this.SetDataItem(item); }
private LoopingListItem CreateVisualItem() { if (this.owner == null) { return(null); } LoopingListItem item = this.owner.CreateVisualItem(); item.Attach(this); this.items.Add(item); return(item); }
/// <summary> /// Provides the behavior for the Arrange pass of Silverlight layout. Classes can override this method to define their own Arrange pass behavior. /// </summary> /// <param name="finalSize">The final area within the parent that this object should use to arrange itself and its children.</param> /// <returns> /// The actual size that is used after the element is arranged in layout. /// </returns> protected override Size ArrangeOverride(Size finalSize) { double position = 0; this.owner.UpdateSelectedVisualIndex(); for (int i = 0; i < this.visualCount; i++) { LoopingListItem item = this.items[i]; Size desired = item.DesiredSize; if (!item.IsEmpty) { Rect rect; if (this.orientation == Orientation.Vertical) { rect = new Rect(0, position, desired.Width, desired.Height); } else { rect = new Rect(position, 0, desired.Width, desired.Height); } item.Arrange(rect); item.ArrangeRect = rect; } position += this.orientation == Orientation.Vertical ? desired.Height : desired.Width; } if (this.isCentered && !this.centeringDuringLayout) { this.centeringDuringLayout = true; if (this.owner != null && this.owner.SelectedVisualIndex != -1) { this.BringIntoView(this.owner.SelectedIndex, this.owner.SelectedVisualIndex); } else { this.CenterMiddleItem(false, false); } } else { this.centeringDuringLayout = false; } return(finalSize); }
private void OnIsLoopingEnabledChanged() { if (this.owner == null) { return; } this.StopOffsetAnimation(false); if (this.items.Count == 0) { return; } if (!this.isLoopingEnabled) { this.UpdateWheel(0, false); for (int i = 0; i < this.items.Count; i++) { LoopingListItem item = this.items[i]; if (i >= this.logicalCount) { item.SetIsEmpty(true); } } var offset = -this.owner.SelectedIndex * this.itemLength; if (this.isCentered) { offset += this.GetSnapOffsetWhenCentered(); } this.UpdateWheel(offset, false); } else { foreach (LoopingListItem item in this.items) { item.SetIsEmpty(false); } this.InvalidateMeasure(); this.UpdateWheel(this.visualOffset, true); } this.owner.UpdateSelection(this.owner.SelectedIndex, this.owner.GetVisualIndex(this.owner.SelectedIndex), LoopingListSelectionChangeReason.Private); }
/// <summary> /// Centers the item at the currently selected index. /// </summary> internal void CenterCurrentItem(bool animate) { if (this.itemsPanel != null) { LoopingListItem item = this.itemsPanel.ItemFromVisualIndex(this.selectedVisualIndex); if (item != null) { this.itemsPanel.CenterItem(item, false, animate); } else { this.itemsPanel.BringIntoView(this.selectedIndex, this.selectedVisualIndex); } } }
private void UpdateItemTemplate(LoopingListDataItem dataItem, LoopingListItem visualItem) { DataTemplate template = null; DataTemplateSelector selector = this.itemTemplateSelectorCache; if (selector != null) { template = selector.SelectTemplate(dataItem, visualItem); } if (template == null) { template = this.itemTemplateCache; } visualItem.ContentTemplate = template; }
/// <summary> /// Creates a <see cref="LoopingListItem"/> instance to be used by the hosted <see cref="LoopingPanel"/>. /// </summary> internal LoopingListItem CreateVisualItem() { LoopingListItem item = this.CreateVisualItemInstance(); item.Width = this.ItemWidth; item.Height = this.ItemHeight; double spacing = this.ItemSpacing; item.Margin = this.orientationCache == Orientation.Vertical ? new Thickness(0, spacing, 0, spacing) : new Thickness(spacing, 0, spacing, 0); if (this.itemStyleCache != null) { item.Style = this.itemStyleCache; } return(item); }
private void CenterMiddleItem(bool select, bool animate) { LoopingListItem middleItem = this.FindMiddleItem(); if (middleItem == null) { if (this.items.Count > 0) { middleItem = this.items[0]; } else { return; } } this.CenterItem(middleItem, select, animate); }
private void OnIsLoopingEnabledChanged() { if (this.owner == null) { return; } this.StopOffsetAnimation(false); if (this.items.Count == 0) { return; } if (!this.isLoopingEnabled) { double normalizedOffset = this.visualOffset % (this.logicalCount * this.itemLength); normalizedOffset = normalizedOffset > 0 ? normalizedOffset - (this.itemLength * this.logicalCount) : normalizedOffset; normalizedOffset = this.ClampOffset(normalizedOffset); this.UpdateWheel(0, false); for (int i = 0; i < this.items.Count; i++) { LoopingListItem item = this.items[i]; if (i > this.logicalCount - 1) { item.SetIsEmpty(true); } } this.UpdateWheel(normalizedOffset, false); } else { foreach (LoopingListItem item in this.items) { item.SetIsEmpty(false); } } this.UpdateWheel(this.visualOffset, true); }
/// <summary> /// Selects the specified visual item. /// </summary> /// <param name="item">The item to be selected.</param> /// <param name="reason">The reason that caused the select request.</param> internal virtual bool SelectItem(LoopingListItem item, LoopingListSelectionChangeReason reason) { if (!this.CanChangeSelectedIndex(item.LogicalIndex)) { return(false); } if (!item.IsSelected) { // update the selected index this.UpdateSelection(item.LogicalIndex, item.VisualIndex, reason); } else { this.UpdateItemsIsSelectedState(); } return(true); }
internal LoopingListItem GetFirstVisibleItem() { LoopingListItem topItem = null; double offset = double.MaxValue; foreach (LoopingListItem item in this.Children) { if (!item.IsEmpty && !item.isHidden) { var itemOffset = this.orientation == Orientation.Horizontal ? item.HorizontalOffset : item.VerticalOffset; if (itemOffset < offset) { offset = itemOffset; topItem = item; } } } return(topItem); }
private void TranslateItems(int startIndex, int endIndex, double offset) { for (int i = startIndex; i < endIndex; i++) { LoopingListItem item = this.items[i]; if (!item.IsEmpty) { if (this.orientation == Orientation.Vertical) { item.SetVerticalOffset(offset); } else { item.SetHorizontalOffset(offset); } if (!this.isLoopingEnabled) { double itemOffset = this.orientation == Orientation.Vertical ? item.VerticalOffset : item.HorizontalOffset; itemOffset = Math.Round(itemOffset, 2); double thisOffset = Math.Round(this.visualOffset, 2); double maxOffset = Math.Round((this.logicalCount * this.itemLength) + this.visualOffset, 2); if (maxOffset <= itemOffset || itemOffset - thisOffset < 0) { item.SetIsHidden(true); } else { item.SetIsHidden(false); } } else { item.SetIsHidden(false); } } this.visualIndexChain.Add(i); } }
private void CreateVisualItems() { int oldCount = this.visualCount; this.UpdateVisualItemCount(); if (this.visualCount == 0 || this.visualCount == oldCount && this.items.Count >= this.visualCount) { return; } if (this.visualCount != oldCount || this.items.Count < this.visualCount) { this.Children.Clear(); this.visualIndexChain.Clear(); this.items.Clear(); } for (int i = 0; i < this.visualCount; i++) { LoopingListItem item = this.CreateVisualItem(); this.Children.Add(item); this.visualIndexChain.Add(i); if (!this.isLoopingEnabled && i > this.logicalCount - 1) { item.SetIsEmpty(true); } } if (this.topLogicalIndex == 0 || this.visualCount != oldCount) { this.UpdateIndexes(this.visualCount != oldCount, false); } else { this.BringIntoView(this.topLogicalIndex, this.visualIndexChain[0]); } }
/// <summary> /// Updates the visual state of a single item. /// </summary> /// <param name="item">The <see cref="LoopingListItem"/> instance which state is to be updated.</param> /// <param name="updateParams">The structure that encapsulates different update parameters such as Animate, EvaluateEnabled, etc.</param> internal virtual void UpdateItemVisualState(LoopingListItem item, VisualStateUpdateParams updateParams) { if (item.IsEmpty) { return; } int logicalIndex = item.LogicalIndex; item.BeginVisualStateUpdate(); if (updateParams.EvaluateEnabled) { item.IsEnabled = this.IsItemEnabled(logicalIndex); } if (updateParams.EvaluateSelected) { item.IsSelected = this.IsItemSelected(logicalIndex); } item.EndVisualStateUpdate(true, updateParams.Animate); }
/// <summary> /// Called before the Tapped event occurs. /// </summary> protected override void OnTapped(TappedRoutedEventArgs e) { base.OnTapped(e); if (e == null) { return; } if (this.skipTapped) { this.skipTapped = false; return; } Point hitPoint = e.GetPosition(this); LoopingListItem tappedItem = this.itemsPanel.ItemFromOffset(this.Orientation == Orientation.Vertical ? hitPoint.Y : hitPoint.X); if (tappedItem != null) { this.OnVisualItemTap(tappedItem); } }
internal void UpdateIndexes(bool force, bool animate) { this.topLogicalIndex = this.GetFirstRealizedIndex(this.logicalCount); int itemCount = this.visualIndexChain.Count; int currentLogicalIndex = this.topLogicalIndex; for (int i = 0; i < itemCount; i++) { LoopingListItem item = this.items[this.visualIndexChain[i]]; if (currentLogicalIndex == this.logicalCount) { currentLogicalIndex = 0; } if (this.owner != null && !item.IsEmpty) { this.owner.UpdateVisualItem(item, currentLogicalIndex, this.visualIndexChain[i], force, animate); } currentLogicalIndex++; } }
/// <summary> /// Handles a Double-tap event from a child <see cref="LoopingListItem"/>. /// </summary> internal virtual void OnVisualItemDoubleTap(LoopingListItem item) { this.SelectTappedItem(item); }
/// <summary> /// Allows to minimize the overhead of creating new instances whenever a logical index changes by formatting an existing data item instance. /// </summary> /// <param name="dataItem"></param> /// <param name="visualItem"></param> internal virtual void UpdateDataItem(LoopingListDataItem dataItem, LoopingListItem visualItem) { this.UpdateItemTemplate(dataItem, visualItem); }