C# 滚动至UWP列表视图中的新项目
我正在使用包含消息的ListView创建聊天应用程序。发送/接收新消息时,ListView应滚动至新消息 我使用的是MVVM,所以ListView看起来像C# 滚动至UWP列表视图中的新项目,c#,.net,xaml,uwp,windows-community-toolkit,C#,.net,Xaml,Uwp,Windows Community Toolkit,我正在使用包含消息的ListView创建聊天应用程序。发送/接收新消息时,ListView应滚动至新消息 我使用的是MVVM,所以ListView看起来像 <ScrollViewer> <ItemsControl Source="{Binding Messages}" /> </ScrollViewer> 我怎么做 编辑:我试图在周年纪念更新之前的版本中实现这一点,创建一个行为。这就是我到目前为止所做的: public class FocusLa
<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);
}
}
}
公共类焦点lastBehavior:Behavior
{
受保护的覆盖无效附加()
{
base.onatached();
AssociatedObject.Items.VectorChanged+=ItemsOnVectorChanged;
}
私有无效项SonVectorChanged(IObservableVector发送方,IVectorChangedVentArgs@event)
{
var scroll=VisualTreeExtensions.findVisualascentant(关联对象);
如果(滚动==null)
{
返回;
}
var last=AssociatedObject.Items.LastOrDefault();
if(last==null)
{
返回;
}
var container=AssociatedObject.ContainerFromItem(最后一个);
ScrollToElement(滚动,(UIElement)容器);
}
私有静态无效ScrollToElement(ScrollViewer ScrollViewer、UIElement元素、,
bool isVerticalScrolling=true,bool smoothScrolling=true,float?zoomFactor=null)
{
var transform=element.TransformToVisual((UIElement)scrollViewer.Content);
变量位置=transform.TransformPoint(新点(0,0));
如果(垂直滚动)
{
scrollViewer.ChangeView(null,position.Y,zoomFactor,!smoothScrolling);
}
其他的
{
scrollViewer.ChangeView(位置.X,null,zoomFactor,!smoothScrolling);
}
}
}
该代码使用来自的VisualTreeExtensions
但是,调用TransformPoint后的位置始终返回{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();
}
if(listview!=null)
{
UpdateLayout();
ScrollIntoView(vm.newmessageItem);
var listViewItem=(FrameworkElement)listview.ContainerFromItem(vm.newmessageItem);
while(listViewItem==null)
{
等待任务。延迟(1);
listViewItem=(FrameworkElement)listview.ContainerFromItem(vm.newmessageItem);
}
ScrollViewer scroll=Utility.findFirstElementVisualTree(listview);
左上角变量=
TransformToVisual(listview).TransformPoint(新点()).Y;
var lvih=listViewItem.ActualHeight;
var lvh=listview.ActualHeight;
var desiredTopLeft=(lvh-lvih);
var currentOffset=scroll.VerticalOffset;
var desiredOffset=当前偏移量+desiredTopLeft;
UpdateLayout();
scroll.ChangeView(null,desiredOffset,null);
scroll.UpdateLayout();
}
从Windows 10开始,您可以使用1607版。使用值KeepLastItemInView
,这似乎是最适合这份工作的
MS UWP文档(2017-2-8)中有一个例子可以归结为以下XAML:
<ListView Source="{Binding Messages}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel
VerticalAlignment="Bottom"
ItemsUpdatingScrollMode="KeepLastItemInView"
/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
这一行代码将自动转到您请求的列表中的项目。例如,如果您有一个聊天客户端,并且您的
ListView
中会填充每个条目。这将在列表底部显示最新条目。所以,用户不必每次都滚动到底部。此处设置为转到最后一项。在UWP应用程序中工作
MyListView?.ScrollIntoView(MyListView.Items[allMyItemsInList.Count - 1], ScrollIntoViewAlignment.Leading);
您是否获得要滚动到的listviewitem?不要在
ScrollViewer
中包装ListView
,它可以自动滚动。要访问其内部ScrollViewer
(例如,要调用其ChangeView
方法以滚动到给定的像素偏移量),请使用ListView.GetScrollViewer()
。您检查了吗?我不知道如何引入任务。在那里延迟。这看起来像是对meAlso的一种攻击,我认为访问ViewModel直接耦合逻辑太多,因此这种行为不会被重用。你能将你的代码发布到你收到消息的地方吗?如果你知道你可以编写一个逻辑来获取listview项,而无需访问VMModel,这是理想的解决方案,但不幸的是,我的项目必须以10586为目标。实施ItemsStackPanel是否可行?在我的案例中有哪些选项?不幸的是项更新纸卷模式
enum在版本10586及以下版本中没有KeepLastItemInView
,因此您必须自己调用ListView.ScrollIntoView
。在何处拨打此电话有多个选项。我可能会选择扩展ListView
并在onitmchanged
覆盖中调用它,类似于.Nice中讨论的WPF解决方案!顺便说一句,我已经切换到ItemsControl,因为我不需要ListView提供的所有选择跟踪功能。ItemsControl也适用吗?关于ScrollViewer,它是否会与ItemsControl而不是ListView冗余?谢谢当然,如果用ItemsControl
替换ListView
,您必须提供自己的滚动处理,并且ScrollViewer
在这里完全有意义。您仍然可以覆盖ItemsControl.onitemchanged
(我喜欢这种方法,因为它允许将UI控件代码与模型完全解耦),但是如果您这样做,您需要将ScrollViewer
引用传递给该方法,或者在可视化树中查找它,然后滚动到底部。请,看看上面的编辑问题。我使用一个行为(为了可重用性)处理它,TransformPoint调用总是返回点0,0,因此它不起作用。
MyListView?.ScrollIntoView(MyListView.Items[allMyItemsInList.Count - 1], ScrollIntoViewAlignment.Leading);