2

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 Effect と、カテゴリ (int) から適切な色への値コンバーターが含まれます。すべてが ListBoxItem の DataTemplate にバインドされています。技術的には、すべて正常に動作します。

ただし、問題はパフォーマンスです。outerGlow ビットマップ効果はプロセッサに負担がかかるようです。そのため、約 500 冊の本のリストがある場合、データベースからデータを取得するのに約 500 ミリ秒かかりますが、アイテムを実際に ListBox にロードするのに約 10 秒かかります。また、読み込みが完了しても、スクロールが非常に遅くなります。VirtualizingStackPanel.IsVirtualizing を True に設定しようとしましたが、役に立ちませんでした。(任意の時点でデータベースに存在できる本の最大数は約 30000 です。)

ただし、リストボックスに 100 を超える項目がある場合でも、人間の頭ではそれほど速く処理できないため、検索されたすべての本を読み込んでユーザーに一覧表示することは目指していません。そのため、実際にリストボックスを ObservableCollection オブジェクトにバインドするラッパー ナビゲーション クラス BookNavigator を作成しました。すべての本がこの BookNavigator にロードされますが、(observableCollection に追加することにより) リストボックスに表示されるのは X 本だけです。

これに関する問題は、リストボックスがスクロールバーを表示しないように、表示される本の数を十分に小さくしたいので、独自のスクロール方法を実装できることです (First、Previous、Next、Last、または単に自分のスクロールバーは、関係ない)。

スクロールバーが表示されないように表示するアイテムの数を計算するにはどうすればよいですか?

ポップアップする 2 つの問題: - アプリケーションのサイズを変更すると、リストボックスのサイズが変わる可能性があります。

私がやろうとしていることを達成する方法はありますか?


EDIT(Martin Harrisへの返信として)

マーティン・ハリスが提案したコードの問題は、foreach ループが FrameworkElement を使用しているが、リストボックスが FrameworkElement から継承されないタイプの Book のオブジェクトで満たされていることであり、その高さを計算する他の手段もありません。ListBoxItem のルート要素はグリッドなので、このグリッドを取得することは可能かもしれませんが、その方法がわかりません。

リストボックス項目を表すために作成された実際の UI 要素を取得する方法はありますか?


編集

私が必要としていると思われるこのQ / Aを見つけました.. ItemContainerGenerator

4

4 に答える 4

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 をループし、そのテストを使用して表示されているものを判断できます。このリストの数は、リストボックスに表示されるアイテムの数を示します。

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:24:32.607 に答える
2

リスト ボックスに追加する前に、おそらくすべての書籍のサイズを把握できますか(おそらく、コード ビハインドでテンプレート 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;
    }
}

リストが大きくなった場合は、ギャップを埋めてプロセスを再実行するのに十分なアイテムをコレクションに追加できます。

于 2009-05-06T15:59:44.833 に答える
-3

解決策はここで暗黙的に見つけることができます: 解決策

于 2009-05-12T18:35:05.733 に答える