2

我有一门课叫书;

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 本书的列表时,从数据库中检索数据大约需要 500 毫秒,但将项目实际加载到 ListBox 大约需要 10 秒。即使加载完成,滚动也很慢。我尝试将 VirtualizingStackPanel.IsVirtualizing 设置为 True,但无济于事。(在任何给定时间,数据库中的最大书籍数量约为 30000。)

但是,即使列表框中有 100 多个项目,人脑也无法快速处理,因此我的目标不是加载并向用户列出所有搜索到的书籍。这就是为什么我创建了一个包装导航类 BookNavigator,它实际上将列表框绑定到它的 ObservableCollection 对象。所有书籍都加载到此 BookNavigator 中,但只有 X 本书显示在列表框中(通过将它们添加到 observableCollection 中)。

这样做的问题是我希望显示的书籍数量足够小,以至于列表框不显示滚动条,所以我可以实现自己的滚动方法(第一个、上一个、下一个、最后一个,或者只是我自己的滚动条,不没关系)。

如何计算要显示的项目数,以便不显示滚动条?

弹出的两个问题: - 调整应用程序的大小可以更改列表框的大小 - 并非所有列表框项目的高度都相同(取决于作者的数量)。

有什么办法可以实现我想要做的事情吗?


编辑(作为对马丁哈里斯的回复)

Martin Harris 建议的代码的问题是 foreach 循环使用 FrameworkElement,但列表框填充了 Book 类型的对象,这些对象不继承自 FrameworkElement,也没有任何其他计算其高度的方法。ListBoxItem 的根元素是一个网格,所以也许可以检索这个网格,但我不知道该怎么做?

有什么方法可以获取为表示列表框项而创建的实际 UI 元素?


编辑

我发现这个 Q/A 似乎是我需要的.. ItemContainerGenerator

4

4 回答 4

5

在试图找出类似的东西之后,我想我会在这里分享我的结果(因为它似乎比其他回复更容易):

我从这里得到的简单能见度测试。

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,然后询问外部控件的大小),这将是很多工作获得。您不能简单地选择一些足以填满列表框的书籍,但又不能选择太多以减慢渲染速度并关闭滚动条吗?这仍然可以与列表框的大小相关联,以便随着列表框的增长添加更多项目,但添加一些额外项目的性能成本应该低于再次计算所有大小的成本。

快速示例:假设一本书的大小为 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 回答
1

“我如何计算要显示多少项目而不显示滚动条?”

简短回答:(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 行数。因此,您只添加“listbox_total_visible_items”项目,列表框将已满且不显示滚动条。

代码说明:

  1. listbox_total_visible_items 包含要显示的行数

  2. 设置适当大小的列表框,仅 2 行

3-7。添加一些行

  1. 跳到最后一行,只是为了好玩

  2. 在表单文本栏上显示列表框行数,基于列表框高度除以每个项目的大小。

就是这样。

于 2017-05-07T21:57:42.880 回答
-3

可以在这里隐式找到解决方案: 解决方案

于 2009-05-12T18:35:05.733 回答