ListView
があり、多くのアイテムが含まれている可能性があるため、virtualized
とリサイクルアイテムです。ソートは使用しません。値の表示を更新する必要がありますが、項目が多すぎると更新が遅すぎるので、表示されている項目のみを更新したいと思います。
現在表示されているすべてのアイテムのリストを取得するにはどうすればよいですか? ListView
またはScrollViewer
を調べようとしましたが、それを実現する方法がまだわかりません。これは遅すぎるため、ソリューションは、すべての項目を調べて、それらが表示されるかどうかをテストする必要はありません。
コードまたはxamlが役立つかどうかはわかりません。これは、Virtualized
がItemSource
にバインドされたArray
/Recycling ListView
です。
編集:回答:
akjoshiのおかげで、私は道を見つけました:
ScrollViewer
のListView
を取得します(FindDescendant
メソッドを使用すると、VisualTreeHelper
を使用して自分で行うことができます)。
そのScrollViewer.VerticalOffset
を読んでください:それは表示された最初のアイテムの番号です
ScrollViewer.ViewportHeight
を読んでください:表示されているアイテムの数です。CanContentScroll
は真でなければなりません。目に見えるListView
アイテムを見つけるためのテクニックを示すMSDNのこの質問を見てください-
実際に表示されているListView内の行(ListViewItem)を見つける方法は?
これがその投稿からの関連コードです-
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);
};
}
}
};
もう1つすべきことは、ObservableCollection
の代わりに ItemSource
をArray
として使用することです。それは間違いなく パフォーマンスを向上させる 。
更新:
そうかもしれませんが(array
vs. ObservableCollection
)、これに関連するいくつかの統計を確認したいと思います。
ObservableCollection
の本当の利点は、実行時にListView
からアイテムを追加/削除する必要がある場合、Array
の場合は再割り当てする必要があることです。 ItemSource
のListView
とListView
は、最初に前のアイテムを破棄し、リスト全体を再生成します。
同様のことを理解しようとした後、私はここで私の結果を共有すると思いました(他の応答よりも簡単に思えるので):
ここ から取得した簡単な可視性テスト。
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;
}
私が物事を見る方法:
一方では、データがあります。これはあなたの情報が記憶にある場所であるため、それらは最新でなければなりません。データリストの反復はかなり高速である必要があり、何よりも、バックグラウンドで別のスレッドで実行できます
反対側には、ディスプレイがあります。 ListView
は、仮想化されているため、表示されているデータのみを更新するというトリックをすでに作成しています。これ以上のトリックは必要ありません、それはすでに実施されています!
最後の作業では、ObservableCollection
にバインディングを使用することをお勧めします。別のスレッドからObservableCollection
を変更する場合は、次のことをお勧めします: http://blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection- binding-part-1 /
私はこれに対するより良い解決策を見つけることに多くの時間を費やしています。私の状況では、表示/非表示に設定できるカスタムの高さのアイテムで満たされたスクロールビューアーがあります。これを思いつきました。上記のソリューションと同じですが、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,
}
仮想化を有効にしている場合ListViewの場合、以下のようにすべての現在の表示アイテムを取得できます。
コードを以下に示します。
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のリストが返されます。それが役に立てば幸い :)。