2017-05-04 39 views
2

我創建一個包含消息的ListView的聊天應用程序。當發送/接收新消息時,ListView應滾動到新消息。滾動到列表視圖中的新項目爲UWP

我使用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 Community Toolkit

使用VisualTreeExtensions然而,位置調用TransformPoint總是返回{0, 0}

後,我在做什麼錯?

+0

你得到listviewitem你想滾動? – Archana

+0

不要將ListView包裝在ScrollViewer中,它可以自動滾動。要訪問其內部的'ScrollViewer'(例如,調用其'ChangeView'方法滾動到給定的像素偏移量),使用'ListView.GetScrollViewer()'。 – andreask

+0

您是否檢查[this](http://stackoverflow.com/questions/16866309/listbox-scroll-into-view-with-mvvm)? – AVK

回答

2

從Windows 10版本1607開始,您可以使用ItemsStackPanelItemsUpdatingScrollMode與值KeepLastItemInView,這似乎是最適合的工作。

有一個在MS UWP文檔的"Inverted Lists"例子(2017年2月8日),這將歸結到這個XAML:

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

在一個側面說明,當然了,我同意你可能希望擺脫ScrollViewer,因爲它是多餘的ListView包裝。

UPD:

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()); 
    } 
} 
+0

這是理想的解決方案,但不幸的是我的項目必須以構建10586爲目標。實現ItemsStackPanel是否可行?在我的情況下有什麼選擇? – SuperJMN

+1

不幸的是'項目更新滾動模式'枚舉沒有'建立10586和以下的'KeepLastItemInView',所以你必須自己調用'ListView.ScrollIntoView'。有多個選項可以放置此呼叫。我可能會選擇擴展ListView,並在OnItemsChanged覆蓋中調用它,類似於[這個問題]中討論的WPF解決方案(http://stackoverflow.com/questions/16866309/listbox-scroll-into-視圖與 - MVVM)。 –

+0

不錯!順便說一句,我已經切換到ItemsControl,因爲我不需要ListView提供的所有選擇跟蹤功能。這也可以用於ItemsControl嗎?關於ScrollViewer,它是否與ItemsControl而不是ListView一樣多餘?謝謝! – SuperJMN

1

這裏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(); 
    } 
+0

我不明白我該如何介紹一項任務。在那裏延期。它看起來像一個黑客 – SuperJMN

+0

此外,我認爲訪問ViewModel直接耦合的邏輯太多,所以這種行爲將不會重複使用。 – SuperJMN

+0

您可以將您的代碼發佈到收到消息的地方嗎? – Archana

相關問題