public static void ScrollIntoViewSmoothly(this ListViewBase listViewBase, object item, ScrollIntoViewAlignment alignment) { if (listViewBase == null) { throw new ArgumentNullException(nameof(listViewBase)); } var scrollViewer = listViewBase.GetDescendantsOfType<ScrollViewer>().First(); var originHorizontalOffset = scrollViewer.HorizontalOffset; var originVerticalOffset = scrollViewer.VerticalOffset; EventHandler<object> layoutUpdatedHandler = null; layoutUpdatedHandler = delegate { listViewBase.LayoutUpdated -= layoutUpdatedHandler; var targetHorizontalOffset = scrollViewer.HorizontalOffset; var targetVerticalOffset = scrollViewer.VerticalOffset; EventHandler<ScrollViewerViewChangedEventArgs> scrollHandler = null; scrollHandler = (sender, e) => { scrollViewer.ViewChanged -= scrollHandler; scrollViewer.ChangeView(targetHorizontalOffset, targetVerticalOffset, null); }; scrollViewer.ViewChanged += scrollHandler; ; scrollViewer.ChangeView(originHorizontalOffset, originVerticalOffset, null, true); }; listViewBase.LayoutUpdated += layoutUpdatedHandler; listViewBase.ScrollIntoView(item, alignment); }
public static async Task ScrollIntoViewAsync(this ListViewBase listViewBase, object item, ScrollIntoViewAlignment alignment = ScrollIntoViewAlignment.Leading) { var tcs = new TaskCompletionSource <object>(); var scrollViewer = listViewBase.GetScrollViewer(); EventHandler <object> layoutUpdated = (s1, e1) => tcs.TrySetResult(null); EventHandler <ScrollViewerViewChangedEventArgs> viewChanged = (s, e) => { scrollViewer.LayoutUpdated += layoutUpdated; scrollViewer.UpdateLayout(); }; try { scrollViewer.ViewChanged += viewChanged; listViewBase.ScrollIntoView(item, alignment); await tcs.Task; } finally { scrollViewer.ViewChanged -= viewChanged; scrollViewer.LayoutUpdated -= layoutUpdated; } }
public static void ScrollIntoViewSmoothly(this ListViewBase listViewBase, object item, ScrollIntoViewAlignment alignment) { if (listViewBase == null) { throw new ArgumentException(nameof(ListViewBase)); } // GetFirstDescendantOfType 是 WinRTXamlToolkit 中的扩展方法, // 寻找该控件在可视树上第一个符合类型的子元素。 ScrollViewer scrollViewer = XamlHelper.FindChildOfType <ScrollViewer>(listViewBase); // 由于 ScrollViewer 肯定有,因此不做 null 检查判断了。 // 记录初始位置,用于 ScrollIntoView 检测目标位置后复原。 double originHorizontalOffset = scrollViewer.HorizontalOffset; double originVerticalOffset = scrollViewer.VerticalOffset; EventHandler <object> layoutUpdatedHandler = null; layoutUpdatedHandler = delegate { listViewBase.LayoutUpdated -= layoutUpdatedHandler; // 获取目标位置。 double targetHorizontalOffset = scrollViewer.HorizontalOffset; double targetVerticalOffset = scrollViewer.VerticalOffset; EventHandler <ScrollViewerViewChangedEventArgs> scrollHandler = null; scrollHandler = delegate { scrollViewer.ViewChanged -= scrollHandler; // 最终目的,带平滑滚动效果滚动到 item。 scrollViewer.ChangeView(targetHorizontalOffset, targetVerticalOffset, null); }; scrollViewer.ViewChanged += scrollHandler; // 复原位置,且不需要使用动画效果。 scrollViewer.ChangeView(originHorizontalOffset, originVerticalOffset, null, true); }; listViewBase.LayoutUpdated += layoutUpdatedHandler; // 跑腿。 listViewBase.ScrollIntoView(item, alignment); }
public async Task GoToGroupAsync(int groupIndex, ScrollIntoViewAlignment scrollIntoViewAlignment = ScrollIntoViewAlignment.Leading) { if (groupCollection != null) { var gc = groupCollection; if (groupIndex < gc.GroupHeaders.Count && groupIndex >= 0 && !isGotoGrouping) { isGotoGrouping = true; //load more so that ScrollIntoViewAlignment.Leading can go to top var loadcount = this.GetVisibleItemsCount() + 1; progressRing.IsActive = true; progressRing.Visibility = Visibility.Visible; //make sure user don't do any other thing at the time. this.IsHitTestVisible = false; //await Task.Delay(3000); while (gc.GroupHeaders[groupIndex].FirstIndex == -1) { if (gc.HasMoreItems) { await gc.LoadMoreItemsAsync(loadcount); } else { break; } } if (gc.GroupHeaders[groupIndex].FirstIndex != -1) { //make sure there are enought items to go ScrollIntoViewAlignment.Leading //this.count > (firstIndex + loadcount) if (scrollIntoViewAlignment == ScrollIntoViewAlignment.Leading) { var more = this.Items.Count - (gc.GroupHeaders[groupIndex].FirstIndex + loadcount); if (gc.HasMoreItems && more < 0) { await gc.LoadMoreItemsAsync((uint)Math.Abs(more)); } } progressRing.IsActive = false; progressRing.Visibility = Visibility.Collapsed; var groupFirstIndex = gc.GroupHeaders[groupIndex].FirstIndex; ScrollIntoView(this.Items[groupFirstIndex], scrollIntoViewAlignment); ListViewItem listViewItem = ContainerFromIndex(groupFirstIndex) as ListViewItem; if (listViewItem != null) { GeneralTransform gt = listViewItem.TransformToVisual(this); var rect = gt.TransformBounds(new Rect(0, 0, listViewItem.ActualWidth, listViewItem.ActualHeight)); //add delta,so that it does not look like suddenly if (rect.Bottom < 0 || rect.Top > this.ActualHeight) { } //already in viewport, maybe it will not change view else { this.IsHitTestVisible = true; isGotoGrouping = false; } } } else { this.IsHitTestVisible = true; isGotoGrouping = false; progressRing.IsActive = false; progressRing.Visibility = Visibility.Collapsed; } } } }
public async Task GoToPreviousGroupAsync(ScrollIntoViewAlignment scrollIntoViewAlignment = ScrollIntoViewAlignment.Leading) { if (groupCollection != null) { var gc = groupCollection; var currentGroupIndex = GetCurrentVisibleGroupIndex(); if (currentGroupIndex - 1 < 0) { currentGroupIndex = gc.GroupHeaders.Count - 1; } else { currentGroupIndex--; } await GoToGroupAsync(currentGroupIndex, scrollIntoViewAlignment); } }
public static async Task ScrollIntoViewAsync(this ListViewBase listViewBase, object item, ScrollIntoViewAlignment alignment, bool updateLayout) { var tcs = new TaskCompletionSource <object>(); var scrollViewer = listViewBase.GetScrollViewer(); void layoutUpdated(object s1, object e1) { tcs.TrySetResult(null); } void viewChanged(object s, ScrollViewerViewChangedEventArgs e) { scrollViewer.LayoutUpdated += layoutUpdated; if (updateLayout) { scrollViewer.UpdateLayout(); } } try { scrollViewer.ViewChanged += viewChanged; listViewBase.ScrollIntoView(item, alignment); await tcs.Task; } finally { scrollViewer.ViewChanged -= viewChanged; scrollViewer.LayoutUpdated -= layoutUpdated; } }
public async Task GoToGroupAsync(int groupIndex, ScrollIntoViewAlignment scrollIntoViewAlignment = ScrollIntoViewAlignment.Leading) { if (groupCollection != null) { var gc = groupCollection; if (groupIndex < gc.GroupHeaders.Count && groupIndex >= 0 && !isGotoGrouping) { isGotoGrouping = true; //load more so that ScrollIntoViewAlignment.Leading can go to top var loadcount = this.GetVisibleItemsCount() + 1; progressRing.IsActive = true; progressRing.Visibility = Visibility.Visible; //make sure user don't do any other thing at the time. this.IsHitTestVisible = false; //await Task.Delay(3000); while (gc.GroupHeaders[groupIndex].FirstIndex == -1) { if (gc.HasMoreItems) { await gc.LoadMoreItemsAsync(loadcount); } else { break; } } if (gc.GroupHeaders[groupIndex].FirstIndex != -1) { //make sure there are enought items to go ScrollIntoViewAlignment.Leading //this.count > (firstIndex + loadcount) if (scrollIntoViewAlignment == ScrollIntoViewAlignment.Leading) { var more = this.Items.Count - (gc.GroupHeaders[groupIndex].FirstIndex + loadcount); if (gc.HasMoreItems && more < 0) { await gc.LoadMoreItemsAsync((uint)Math.Abs(more)); } } progressRing.IsActive = false; progressRing.Visibility = Visibility.Collapsed; var groupFirstIndex = gc.GroupHeaders[groupIndex].FirstIndex; ScrollIntoView(this.Items[groupFirstIndex], scrollIntoViewAlignment); //already in viewport, maybe it will not change view if (groupDic.ContainsKey(gc.GroupHeaders[groupIndex]) && groupDic[gc.GroupHeaders[groupIndex]].Visibility == Visibility.Visible) { this.IsHitTestVisible = true; isGotoGrouping = false; } } else { this.IsHitTestVisible = true; isGotoGrouping = false; progressRing.IsActive = false; progressRing.Visibility = Visibility.Collapsed; } } } }
public async ValueTask ScrollIntoViewAsync(ElementReference element, ScrollIntoViewAlignment block, ScrollIntoViewAlignment inline, bool smooth = false) { var module = await _jsRuntime .ImportOrGetModuleAsync(ModulePath, ModuleKey, _jsRefStore); await module.InvokeVoidAsync( "scrollIntoView", element, MapScrollIntoViewAlignment(block), MapScrollIntoViewAlignment(inline), smooth); }
public void ScrollIntoView(ElementReference element, ScrollIntoViewAlignment block, ScrollIntoViewAlignment inline, bool smooth = false) { _jsInProcessObjectRef !.InvokeVoid( "scrollIntoView", element, MapScrollIntoViewAlignment(block), MapScrollIntoViewAlignment(inline), smooth); }
public async Task ScrollToItem(object item, VerticalAlignment alignment, bool highlight, double?pixel = null, ScrollIntoViewAlignment direction = ScrollIntoViewAlignment.Leading, bool?disableAnimation = null) { var scrollViewer = ScrollingHost; if (scrollViewer == null) { Logs.Logger.Debug(Logs.Target.Chat, "ScrollingHost == null"); return; } // We are going to try two times, as the first one seem to fail sometimes // leading the chat to the wrong scrolling position var iter = 2; var selectorItem = ContainerFromItem(item) as SelectorItem; while (selectorItem == null && iter > 0) { Logs.Logger.Debug(Logs.Target.Chat, string.Format("selectorItem == null, {0} try", iter + 1)); // call task-based ScrollIntoViewAsync to realize the item await this.ScrollIntoViewAsync(item, direction); // this time the item shouldn't be null again selectorItem = ContainerFromItem(item) as SelectorItem; iter--; } if (selectorItem == null) { Logs.Logger.Debug(Logs.Target.Chat, "selectorItem == null, abort"); return; } // 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)); // If position is negative layout should still happen, // Lets wait for it. if (position.Y < 0) { Logs.Logger.Debug(Logs.Target.Chat, "position.Y is negative, let's wait for layout"); // call task-based ScrollIntoViewAsync to realize the item await this.ScrollIntoViewAsync(item, direction); // this time the item shouldn't be null again selectorItem = ContainerFromItem(item) as SelectorItem; if (selectorItem == null) { Logs.Logger.Debug(Logs.Target.Chat, "selectorItem == null after layout, abort"); return; } transform = selectorItem.TransformToVisual((UIElement)scrollViewer.Content); position = transform.TransformPoint(new Point(0, 0)); } if (alignment == VerticalAlignment.Top) { if (pixel is double adjust) { position.Y -= adjust; } } else if (alignment == VerticalAlignment.Center) { if (selectorItem.ActualHeight < ActualHeight - 48) { position.Y -= (ActualHeight - selectorItem.ActualHeight) / 2d; } else { position.Y -= 48 + 4; } } else if (alignment == VerticalAlignment.Bottom) { position.Y -= ActualHeight - selectorItem.ActualHeight; if (pixel is double adjust) { position.Y += adjust; } } if (highlight) { var bubble = selectorItem.Descendants <MessageBubble>().FirstOrDefault() as MessageBubble; if (bubble != null) { bubble.Highlight(); } } // scroll to desired position with animation! await scrollViewer.ChangeViewAsync(null, position.Y, disableAnimation ?? alignment != VerticalAlignment.Center); //scrollViewer.ChangeView(null, position.Y, null, disableAnimation ?? alignment != VerticalAlignment.Center); }