ListBoxで表示されるアイテムの数を計算する
質問
Bookというクラスがあります;
class Book
{
public string Name { get; set; }
public string Author { get; set; }
public int PagesCount { get; set; }
public int Category { get; set; }
}
ListBoxはブックのリストを表示し、ItemTemplateはブックを視覚的に表すように変更されています。テキストには、本の名前、著者、ページ数が表示されます。ただし、カテゴリは特定の色(たとえば、履歴が青、ロマンスが赤など)で表されます。現在、テキストにはOuterGlowBitmap効果があり、カテゴリ(int)から適切な色への値コンバーターがあります。すべてがListBoxItemのDataTemplateにバインドされています。技術的には、すべて正常に動作します。
ただし、問題はパフォーマンスです。 outerGlowビットマップ効果はプロセッサーに重いようです。したがって、約500冊の本のリストがある場合、データベースからデータを取得するのに約500msかかりますが、実際にListBoxにアイテムをロードするには約10秒かかります。また、読み込みが完了しても、スクロールは非常に遅くなります。 VirtualizingStackPanel.IsVirtualizingをTrueに設定しようとしましたが、効果がありません。 (任意の時点でデータベースに保存できる書籍の最大数は約30000です。)
ただし、リストボックスに100を超えるアイテムがある場合でも、人間の心はそれほど速く処理できないため、検索されるすべての書籍をユーザーに読み込んで一覧表示することは目指していません。そのため、実際にリストボックスをObservableCollectionオブジェクトにバインドするラッパーナビゲーションクラスBookNavigatorを作成しました。すべての書籍はこのBookNavigatorにロードされますが、リストボックスにはそれらのXのみが表示されます(observableCollectionに追加することにより)。
これに関する問題は、リストボックスがスクロールバーを表示しないように、表示される本の数を十分に少なくしたいので、スクロールの独自のメソッド(First、Previous、Next、Last、または単に自分のスクロールバー、重要ではありません)。
スクロールバーが表示されないように表示するアイテムの数を計算するにはどうすればよいですか?
ポップアップする2つの問題:-アプリケーションのサイズを変更すると、リストボックスのサイズを変更できる -すべてのリストボックスアイテムの高さが同じではありません(作成者の数によって異なります)。
私がやろうとしていることを達成する方法はありますか?
編集(Martin Harrisへの返信として)
Martin Harrisが示唆したコードの問題は、foreachループがFrameworkElementを使用しているが、リストボックスはFrameworkElementを継承せず、高さを計算する他の手段を持たないBook型のオブジェクトで満たされていることです。 ListBoxItemのルート要素はグリッドなので、このグリッドを取得することは可能かもしれませんが、その方法はわかりませんか?
リストボックスアイテムを表すために作成された実際のUI要素を取得する方法はありますか?
編集
このQ / Aが見つかりました。 ItemContainerGenerator
解決 4
解決策は暗黙的にここにあります: ソリューション
他のヒント
似たようなことを理解しようとした後、ここで自分の結果を共有しようと思いました(他の応答よりも簡単そうです):
こちら。
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をループし、そのテストを使用して、表示されているものを判別できます。このリストの数は、リストボックスに表示されるアイテムの数を示します。
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;
}
これには、リストボックスに現在表示されているアイテムのリストが含まれます(スクロールなどで非表示になっているものを含む)。
リストボックスに追加する前にすべての書籍のサイズをできます(おそらく、コードXでテンプレートXAMLを解析および設定し、外部コントロールにそのサイズを要求することにより)これはあまり利益を得るための多くの作業になります。リストボックスを満たすのに十分な数の本を選択するだけで、レンダリングを遅くしてスクロールバーをオフにするほど多くはありませんか?これはまだリストボックスのサイズにリンクされているため、アイテムが増えるにつれて追加されるアイテムが増えますが、いくつかのアイテムを追加するためのパフォーマンスコストは、すべてのサイズを再計算するコストよりも低くなります。
簡単な例:著者が1人いる本のサイズが150ピクセルだとします。リストボックスのサイズを取得して125で除算すると、球場にあるアイテムの数の概算が得られますが、計算にコストはかかりません。避けたいのは、アイテムが少なすぎることです。空のスペースが残るからです。
コメントを考慮して編集。
その場合、リストボックスにアイテムを追加し(上記の方法を使用して詳細な推測を取得)、最後に完全に表示されるアイテムを計算し、余分なアイテムを削除します。
この拡張メソッドは、リストボックスに完全に表示される最後の要素を取得します。アイテムが表示されていない場合はnullを取得します。
public static class ListBoxExtensions
{
public static FrameworkElement GetLastItem(this ListBox listBox)
{
double height = listBox.ActualHeight;
double currentHeight = 0;
FrameworkElement previous = null;
foreach (FrameworkElement item in listBox.Items)
{
currentHeight += item.ActualHeight;
if (currentHeight > height)
{
return previous;
}
previous = item;
}
return previous;
}
}
リストを大きくした場合、十分なアイテムをコレクションに追加してギャップを埋め、プロセスを再実行できます。
&quot;スクロールバーが表示されないように表示するアイテムの数を計算するにはどうすればよいですか? &quot;
簡単な答え:(listBox1.Height / listBox1.ItemHeight)
これにより、表示/使用可能な行数が得られるため、この行数のみを読み取ることができ、すべてのリストボックスがいっぱいになります。
今、デモ:
int listbox_total_visible_lines = 2;
listBox1.Height = (listbox_total_visible_lines + 1) * listBox1.ItemHeight;
listBox1.Items.Add("um");
listBox1.Items.Add("dois");
listBox1.Items.Add("tres");
listBox1.Items.Add("quatro");
listBox1.Items.Add("cinco");
listBox1.SelectedIndex = listBox1.Items.Count - 1;
this.Text = (listBox1.Height/ listBox1.ItemHeight).ToString();
この例では、表示されるアイテムの数を選択できるため、実際に表示できる行数がosです。したがって、&quot; listbox_total_visible_items&quot;のみを追加します。アイテムとリストボックスはいっぱいになり、スクロールバーは表示されません。
コードの説明:
-
listbox_total_visible_itemsには、表示する行数が含まれています
-
2行のみの適切なサイズでリストボックスをセットアップする
3-7。行を追加
-
楽しみのために、最後の行に移動
-
フォームのテキストバーに、リストボックスの行数を、リストボックスの高さを各アイテムのサイズで割って表示します。
それだけです。