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