滚动到 UWP 的 ListView 中的新项目
Scroll to new item in ListView for UWP
我正在使用包含消息的 ListView 创建聊天应用程序。当新消息 sent/received 时,ListView 应滚动到新消息。
我正在使用 MVVM,所以 ListView 看起来像
<ScrollViewer>
<ItemsControl Source="{Binding Messages}" />
</ScrollViewer>
我该怎么做?
编辑:我试图在创建行为的周年纪念更新之前的版本中完成这项工作。这是我目前所拥有的:
public class FocusLastBehavior : Behavior<ItemsControl>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Items.VectorChanged += ItemsOnVectorChanged;
}
private void ItemsOnVectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs @event)
{
var scroll = VisualTreeExtensions.FindVisualAscendant<ScrollViewer>(AssociatedObject);
if (scroll == null)
{
return;
}
var last = AssociatedObject.Items.LastOrDefault();
if (last == null)
{
return;
}
var container = AssociatedObject.ContainerFromItem(last);
ScrollToElement(scroll, (UIElement)container);
}
private static void ScrollToElement(ScrollViewer scrollViewer, UIElement element,
bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null)
{
var transform = element.TransformToVisual((UIElement)scrollViewer.Content);
var position = transform.TransformPoint(new Point(0, 0));
if (isVerticalScrolling)
{
scrollViewer.ChangeView(null, position.Y, zoomFactor, !smoothScrolling);
}
else
{
scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling);
}
}
}
中的 VisualTreeExtensions
但是调用TransformPoint后的位置总是returns{0, 0}
我做错了什么?
这里vm.newmessageItem是新消息。我用它来获取你想要滚动的 listviewitem。
if (listview != null)
{
listview.UpdateLayout();
listview.ScrollIntoView(vm.newmessageItem);
var listViewItem = (FrameworkElement)listview.ContainerFromItem(vm.newmessageItem);
while (listViewItem == null)
{
await Task.Delay(1);
listViewItem = (FrameworkElement)listview.ContainerFromItem(vm.newmessageItem);
}
ScrollViewer scroll = Utility.FindFirstElementInVisualTree<ScrollViewer>(listview);
var topLeft =
listViewItem .TransformToVisual(listview) .TransformPoint(new Point()).Y;
var lvih = listViewItem.ActualHeight;
var lvh = listview.ActualHeight;
var desiredTopLeft = (lvh - lvih) ;
var currentOffset = scroll.VerticalOffset;
var desiredOffset = currentOffset + desiredTopLeft;
listview.UpdateLayout();
scroll.ChangeView(null, desiredOffset,null);
scroll.UpdateLayout();
}
从 Windows 10 版本 1607 开始,您可以使用 ItemsStackPanel
.ItemsUpdatingScrollMode
和值 KeepLastItemInView
,这似乎最适合这项工作。
MS UWP 文档 (2017-2-8) 中有一个 "Inverted Lists" 示例可以归结为 XAML:
<ListView Source="{Binding Messages}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel
VerticalAlignment="Bottom"
ItemsUpdatingScrollMode="KeepLastItemInView"
/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
附带说明,是的,我同意您可能想要摆脱 ScrollViewer
,因为它作为 ListView
包装器是多余的。
更新:
KeepLastItemInView
不适用于在 "Anniversary Edition" 之前以 Windows 10 为目标的应用程序。如果是这种情况,确保列表在项目集合更改后始终显示最后一项的一种方法是覆盖 OnItemsChanged
and call ScrollIntoView
。基本实现如下所示:
using System.Linq;
using Windows.UI.Xaml.Controls;
public class ChatListView : ListView
{
protected override void OnItemsChanged(object e)
{
base.OnItemsChanged(e);
if(Items.Count > 0) ScrollIntoView(Items.Last());
}
}
这一行代码将自动转到您请求的列表中的项目。例如,如果您有一个聊天客户端并且您的 ListView
会填充每个条目。这将在列表底部显示最新条目。因此,用户不必每次都滚动到底部。这里设置为转到最后一项。在 UWP 应用程序中工作。
MyListView?.ScrollIntoView(MyListView.Items[allMyItemsInList.Count - 1], ScrollIntoViewAlignment.Leading);
我正在使用包含消息的 ListView 创建聊天应用程序。当新消息 sent/received 时,ListView 应滚动到新消息。
我正在使用 MVVM,所以 ListView 看起来像
<ScrollViewer>
<ItemsControl Source="{Binding Messages}" />
</ScrollViewer>
我该怎么做?
编辑:我试图在创建行为的周年纪念更新之前的版本中完成这项工作。这是我目前所拥有的:
public class FocusLastBehavior : Behavior<ItemsControl>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Items.VectorChanged += ItemsOnVectorChanged;
}
private void ItemsOnVectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs @event)
{
var scroll = VisualTreeExtensions.FindVisualAscendant<ScrollViewer>(AssociatedObject);
if (scroll == null)
{
return;
}
var last = AssociatedObject.Items.LastOrDefault();
if (last == null)
{
return;
}
var container = AssociatedObject.ContainerFromItem(last);
ScrollToElement(scroll, (UIElement)container);
}
private static void ScrollToElement(ScrollViewer scrollViewer, UIElement element,
bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null)
{
var transform = element.TransformToVisual((UIElement)scrollViewer.Content);
var position = transform.TransformPoint(new Point(0, 0));
if (isVerticalScrolling)
{
scrollViewer.ChangeView(null, position.Y, zoomFactor, !smoothScrolling);
}
else
{
scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling);
}
}
}
中的 VisualTreeExtensions
但是调用TransformPoint后的位置总是returns{0, 0}
我做错了什么?
这里vm.newmessageItem是新消息。我用它来获取你想要滚动的 listviewitem。
if (listview != null)
{
listview.UpdateLayout();
listview.ScrollIntoView(vm.newmessageItem);
var listViewItem = (FrameworkElement)listview.ContainerFromItem(vm.newmessageItem);
while (listViewItem == null)
{
await Task.Delay(1);
listViewItem = (FrameworkElement)listview.ContainerFromItem(vm.newmessageItem);
}
ScrollViewer scroll = Utility.FindFirstElementInVisualTree<ScrollViewer>(listview);
var topLeft =
listViewItem .TransformToVisual(listview) .TransformPoint(new Point()).Y;
var lvih = listViewItem.ActualHeight;
var lvh = listview.ActualHeight;
var desiredTopLeft = (lvh - lvih) ;
var currentOffset = scroll.VerticalOffset;
var desiredOffset = currentOffset + desiredTopLeft;
listview.UpdateLayout();
scroll.ChangeView(null, desiredOffset,null);
scroll.UpdateLayout();
}
从 Windows 10 版本 1607 开始,您可以使用 ItemsStackPanel
.ItemsUpdatingScrollMode
和值 KeepLastItemInView
,这似乎最适合这项工作。
MS UWP 文档 (2017-2-8) 中有一个 "Inverted Lists" 示例可以归结为 XAML:
<ListView Source="{Binding Messages}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel
VerticalAlignment="Bottom"
ItemsUpdatingScrollMode="KeepLastItemInView"
/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
附带说明,是的,我同意您可能想要摆脱 ScrollViewer
,因为它作为 ListView
包装器是多余的。
更新:
KeepLastItemInView
不适用于在 "Anniversary Edition" 之前以 Windows 10 为目标的应用程序。如果是这种情况,确保列表在项目集合更改后始终显示最后一项的一种方法是覆盖 OnItemsChanged
and call ScrollIntoView
。基本实现如下所示:
using System.Linq;
using Windows.UI.Xaml.Controls;
public class ChatListView : ListView
{
protected override void OnItemsChanged(object e)
{
base.OnItemsChanged(e);
if(Items.Count > 0) ScrollIntoView(Items.Last());
}
}
这一行代码将自动转到您请求的列表中的项目。例如,如果您有一个聊天客户端并且您的 ListView
会填充每个条目。这将在列表底部显示最新条目。因此,用户不必每次都滚动到底部。这里设置为转到最后一项。在 UWP 应用程序中工作。
MyListView?.ScrollIntoView(MyListView.Items[allMyItemsInList.Count - 1], ScrollIntoViewAlignment.Leading);