4

我正在使用包含消息的 ListView 创建一个聊天应用程序。当发送/接收新消息时,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);
        }
    }
}

该代码使用UWP 社区工具包中的 VisualTreeExtensions

但是,调用 TransformPoint 后的位置总是返回{0, 0}

我究竟做错了什么?

4

3 回答 3

7

从 Windows 10 版本 1607 开始,您可以使用ItemsStackPanel. ItemsUpdatingScrollMode与 value KeepLastItemInView,这似乎是最适合这份工作的。

MS UWP 文档 (2017-2-8) 中有一个“倒排列表”示例,可以归结为以下 XAML:

<ListView Source="{Binding Messages}">
   <ListView.ItemsPanel>
       <ItemsPanelTemplate>
           <ItemsStackPanel
               VerticalAlignment="Bottom"
               ItemsUpdatingScrollMode="KeepLastItemInView"
           />
       </ItemsPanelTemplate>
   </ListView.ItemsPanel>
</ListView>

在旁注中,是的,我同意您可能想要摆脱 a ScrollViewer,因为它作为ListView包装器是多余的。

更新:

KeepLastItemInView不适用于面向“周年纪念版”之前的 Windows 10 的应用程序。如果是这种情况,确保列表在项目集合更改后始终显示最后一项的一种方法是覆盖OnItemsChanged并调用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());
    }
}
于 2017-05-04T21:58:06.187 回答
1

这里 vm.newmessageItem 是新消息。我用它来获取要滚动的列表视图项。

   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();
    }
于 2017-05-04T11:34:11.747 回答
0

这一行代码将自动转到您请求的列表中的项目。例如,如果您有一个聊天客户端,并且您ListView的每个条目都会填充。这将在列表底部显示最新条目。因此,用户不必每次都滚动到底部。此处设置为转到最后一项。在 UWP 应用程序中工作。

MyListView?.ScrollIntoView(MyListView.Items[allMyItemsInList.Count - 1], ScrollIntoViewAlignment.Leading);
于 2018-08-23T01:43:16.243 回答