2012-06-25 46 views
11

我有一個ListView它可能包含很多項目,所以它是virtualized和回收項目。它不使用排序。我需要刷新一些值顯示,但是當項目太多時,更新所有內容太慢,所以我只想刷新可見項目。獲取列表查看可見項

我怎樣才能得到所有當前顯示的項目清單?我試圖查看ListViewScrollViewer,但我仍然不知道如何實現此目的。解決方案不能通過所有項目來測試它們是否可以被看到,因爲這太慢了。

我不知道代碼或XAML將是有益的,它只是一個Virtualized/Recycling ListView綁定到一個ArrayItemSource

編輯: 答:
感謝akjoshi,我找到了辦法:

  • 得到ListView (的ScrollViewerFindDescendant方法,你可以做你自己與VisualTreeHelper )。

  • 閱讀其ScrollViewer.VerticalOffset:這是顯示

  • 的第一個項目的數量讀取其ScrollViewer.ViewportHeight:這是顯示的項目數。
    Rq:CanContentScroll必須爲真。
+1

你如何填你的ListView?顯式創建ListViewItem? ItemSource的設置?捆綁 ?或許給我們一些代碼! –

回答

5

看一看MSDN上這個問題呈現出技術來找出可見ListView項目 -

How to find the rows (ListViewItem(s)) in a ListView that are actually visible?

下面是該職位的相關代碼 -

listView.ItemsSource = from i in Enumerable.Range(0, 100) select "Item" + i.ToString(); 
listView.Loaded += (sender, e) => 
{ 
    ScrollViewer scrollViewer = listView.GetVisualChild<ScrollViewer>(); //Extension method 
    if (scrollViewer != null) 
    { 
     ScrollBar scrollBar = scrollViewer.Template.FindName("PART_VerticalScrollBar", scrollViewer) as ScrollBar; 
     if (scrollBar != null) 
     { 
      scrollBar.ValueChanged += delegate 
      { 
       //VerticalOffset and ViweportHeight is actually what you want if UI virtualization is turned on. 
       Console.WriteLine("Visible Item Start Index:{0}", scrollViewer.VerticalOffset); 
       Console.WriteLine("Visible Item Count:{0}", scrollViewer.ViewportHeight); 
      }; 
     } 
    } 
}; 

另一個你應該做的事情是使用ObservableCollection作爲你的ItemSource而不是Array;那肯定會是improve the performance

更新:

雅那可能是真的(arrayObservableCollection),但我希望看到與此相關的一些統計數據;

ObservableCollection真正的好處是在運行時,如果你有一個要求添加/刪除您ListView項目,在Array的情況下,你將不得不重新分配的ListViewListView第一扔掉它以前的ItemSource項目並重新生成其整個列表。

+1

Rq:對於性能和內存使用情況,陣列超過Observable集合的數量非常大,適合我的應用程序。我使用的項目數量在100.000-1.000.000範圍內。 – GameAlchemist

+0

您提供的MS鏈接將List與Observable集合相比較,具有較低的商品數量(1000)且關閉虛擬化,這很可能是因爲沒有其他可見的差異。所以它不適用於我的情況,我不知道它是否與任何情況有關(爲什麼有人將虛擬化關閉?) – GameAlchemist

+0

@VincentPiel:查看我的更新。 – akjoshi

1

如何我看到的東西:

  • 在一邊,你有你的數據。他們必須是最新的,因爲這是你的信息記憶的地方。在你的數據列表上迭代應該是相當快的,最重要的是,可以在另一個線程上完成,在另一側,你可以在顯示器上進行背景

  • 。您的ListView已經成爲只刷新顯示的數據的技巧,因爲它正在虛擬化!你不需要更多的技巧,它已經到位!

在上次的工作中,在ObservableCollection上使用綁定是一個很好的建議。如果您打算從另一個線程修改ObservableCollection,我會推薦這:http://blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection-binding-part-1/

+0

如果你不知道它,你可能會對MVVM模式感興趣;) –

+1

我沒有調查什麼是使用CPU,但迭代通過我的列表和NotifyPropertyChanged在一個屬性上的所有項目太重了(慢)計算機必須執行我的程序的任務。該列表可能是100.000個項目。所以虛擬化不會節省一天的時間。我測試了ObservableCollection比我的應用中的數組慢5倍以上。 – GameAlchemist

+0

訪問數組的速度確實比訪問ObservableCollection快。但ObservableCollection會使用綁定來完成保持UI最新的所有工作。根據我的經驗,大部分時間都需要創建新的圖形項目。不是數據列表背後的工作。 –

5

試圖找出類似的東西后,我想我會在這裏分享我的結果(因爲它似乎比其他的反應更容易):

簡單的能見度測試我從here得到。

private static bool IsUserVisible(FrameworkElement element, FrameworkElement container) 
{ 
    if (!element.IsVisible) 
     return false; 

    Rect bounds = 
     element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight)); 
    var rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight); 
    return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight); 
} 

之後,您可以遍歷listboxitems並使用該測試來確定哪些是可見的。由於listboxitems總是按照相同的順序排列,因此列表中的第一個可見列表項將成爲用戶的第一個可見列表項。

private List<object> GetVisibleItemsFromListbox(ListBox listBox, FrameworkElement parentToTestVisibility) 
{ 
    var items = new List<object>(); 

    foreach (var item in PhotosListBox.Items) 
    { 
     if (IsUserVisible((ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item), parentToTestVisibility)) 
     { 
      items.Add(item); 
     } 
     else if (items.Any()) 
     { 
      break; 
     } 
    } 

    return items; 
} 
0

我花了很多時間尋找這更好的解決辦法, 在我的情況,我有一個ScrollViewer中,充滿了定製heigths項目可以設置有形/無形的,我想出了這個。它與上述解決方案相同,只是佔用一部分CPU。我希望它對一個人有幫助。 列表視圖/ scrollpanel的第一個項目是TopVisibleItem

public int TopVisibleItem { get; private set; } 

    private double CurrentDistance; 

    private void TouchScroller_ScrollChanged(object sender, ScrollChangedEventArgs e) 
    { 
     if (myItemControl.Items.Count > 0) 
     { 
      MoveDirection direction = (MoveDirection)Math.Sign(e.VerticalChange); 
      if (direction == MoveDirection.Positive) 
       while (CurrentDistance < e.VerticalOffset && TopVisibleItem < myItemControl.Items.Count) 
       { 
        CurrentDistance += ((FrameworkElement)myItemControl.Items[TopVisibleItem]).ActualHeight; 
        TopVisibleItem += 1; 
       } 
      else 
       while (CurrentDistance >= e.VerticalOffset && TopVisibleItem > 0) 
       { 
        CurrentDistance -= ((FrameworkElement)myItemControl.Items[TopVisibleItem]).ActualHeight; 
        TopVisibleItem -= 1; 
       } 
     } 
    } 


    public enum MoveDirection 
    { 
     Negative = -1, 
     Positive = 1, 
    }