16

たくさんのアイテムが入ってListViewいるかもしれないのでvirtualized、リサイクルアイテムです。並べ替えは使用しません。値の表示を更新する必要がありますが、項目が多すぎると更新が遅すぎるため、表示されている項目のみを更新したいと思います。

現在表示されているすべてのアイテムのリストを取得するにはどうすればよいですか?ListViewまたはを調べてみましたがScrollViewer、それを実現する方法がまだわかりません。ソリューションは、すべての項目を調べて、それらが表示されるかどうかをテストする必要はありません。これは遅すぎるためです。

コードまたはxamlが役立つかどうかはわかりませんが、これはVirtualized/Recycling ListViewItemSourceバインドされただけArrayです。

編集: 回答:
akjoshiのおかげで、私は方法を見つけました:

  • のを取得しScrollViewerますListViewFindDescendantメソッドを使用して、で自分で実行できますVisualTreeHelper)。

  • そのを読んでくださいScrollViewer.VerticalOffset:それは表示された最初のアイテムの番号です

  • そのを読んでくださいScrollViewer.ViewportHeight:それは表示されたアイテムの数です。
    Rq:CanContentScroll真でなければなりません。
4

5 に答える 5

8

目に見えるListViewアイテムを見つけるためのテクニックを示すMSDNのこの質問を見てください-

ListViewで実際に表示されている行(ListViewItem(s))を見つける方法は?

これがその投稿からの関連コードです-

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あなたとして使うことです; それは間違いなくパフォーマンスを向上させますItemSourceArray

アップデート:

そうかもしれませんが(arrayvs. ObservableCollection)、これに関連するいくつかの統計を確認したいと思います。

の本当の利点は、実行時にObservableCollectionアイテムを追加/削除する必要がある場合です。その場合、のを再割り当てする必要があり、最初に前のアイテムを破棄してリスト全体を再生成します。ListViewArrayItemSourceListViewListView

于 2012-06-25T10:48:13.953 に答える
8

同様のことを理解しようとした後、私はここで私の結果を共有すると思いました(他の応答よりも簡単に見えるので):

ここから得た簡単な視認性テスト。

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;
}
于 2012-09-28T14:28:28.660 に答える
1

私が物事をどのように見るか:

  • 一方では、データがあります。これはあなたの情報が記憶にある場所であるため、それらは最新でなければなりません。データリストの反復はかなり高速である必要があり、何よりも、バックグラウンドで別のスレッドで実行できます

  • 反対側には、ディスプレイがあります。仮想ListView化されているため、表示されているデータのみを更新するというトリックをすでに作成しています。これ以上のトリックは必要ありません、それはすでに実施されています!

最後の作業では、にバインディングを使用することをObservableCollectionお勧めします。ObservableCollection別のスレッドからを変更する場合は、次のことをお勧めします:http: //blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection-binding-part-1/

于 2012-06-25T11:24:20.950 に答える
0

私はこれに対するより良い解決策を見つけることに多くの時間を費やしています。私の状況では、可視/不可視に設定できるカスタムの高さのアイテムで満たされたスクロールビューアがあり、これを思いつきました。上記のソリューションと同じですが、CPUの一部を使用します。私はそれが誰かを助けることを願っています。リストビュー/スクロールパネルの最初のアイテムは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,
    }
于 2017-09-01T10:19:08.490 に答える
0

仮想化が有効になっているListViewを使用している場合は、次のようにすべてのCurrentVisibleアイテムを取得できます。

  1. VirtualizingStackPanelを入手する
  2. VirtualizingStackPanelのすべてのListViewItemを取得します

コードを以下に示します。

VirtualizingStackPanel virtualizingStackPanel = FindVisualChild<VirtualizingStackPanel>(requiredListView);
List<ListViewItem> items = GetVisualChildren<ListViewItem>(virtualizingStackPanel);

機能を以下に示します。

private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is childItem)
            return (childItem)child;
        else
        {
            childItem childOfChild = FindVisualChild<childItem>(child);
            if (childOfChild != null)
                return childOfChild;
        }
    }
    return null;
}

private List<childItem> GetVisualChildren<childItem>(DependencyObject obj) where childItem : DependencyObject
{
    List<childItem> childList = new List<childItem>();
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is childItem)
            childList.Add(child as childItem);
    }

    if (childList.Count > 0)
        return childList;

    return null;
}

これにより、表示用にロードされた現在のListViewItemのリストが返されます。それが役に立てば幸い :)。

于 2018-05-05T05:39:33.583 に答える