我有一个名为Book的课程;
class Book
{
public string Name { get; set; }
public string Author { get; set; }
public int PagesCount { get; set; }
public int Category { get; set; }
}
ListBox显示Books列表,并且ItemTemplate已被修改,以便直观地表示Book。文本显示了书的名称,作者和页数。然而,类别由某种颜色表示(例如,历史是蓝色,浪漫是红色等)。现在,文本具有OuterGlowBitmap效果和从Category(int)到适当Color的值转换器。所有内容都绑定在DataTemplate for ListBoxItem中。从技术上讲,一切正常。
但问题在于性能。似乎outerGlow位图效果在处理器上很重,所以当我有大约500本书的列表时,从数据库中检索数据需要大约500ms,但实际将项目加载到ListBox大约需要10秒。即使加载完成,滚动也非常滞后。我试图将VirtualizingStackPanel.IsVirtualizing设置为True,但无济于事。 (在任何给定时间,数据库中可以包含的最大书籍数量约为30000。)
然而,即使列表框中有超过100个项目,人类思维也无法快速处理,因此我不打算加载并向用户列出所有搜索到的书籍。这就是为什么我创建了一个包装器导航类BookNavigator,它实际上将列表框绑定到它的ObservableCollection对象。所有书籍都被加载到这个BookNavigator中,但它们中只有X个显示在列表框中(通过将它们添加到observableCollection)。
这个问题是我希望显示的书籍数量足够小,以便列表框不显示滚动条,所以我可以实现我自己的滚动方法(第一个,上一个,下一个,最后一个,或者只是我自己的滚动条,没关系)。
如何计算要显示的项目数,以便不显示滚动条?
弹出两个问题: - 调整应用程序大小可以更改列表框的大小 - 并非所有列表框项目都具有相同的高度(取决于作者的数量)。
有没有办法实现我想做的事情?
编辑(作为对Martin Harris的回复)
代码Martin Harris建议的问题是foreach循环使用FrameworkElement,但是listbox填充了Book类型的对象,它不从FrameworkElement继承,也没有任何其他计算高度的方法。 ListBoxItem的根元素是一个网格,所以也许可以检索这个网格,但我不知道该怎么做?
有没有办法获得为表示列表框项而创建的实际UI元素?
修改
我发现这个Q / A似乎是我需要的...... ItemContainerGenerator
答案 0 :(得分: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;
}
这将包含列表框中当前显示的项目列表(包括通过滚动或类似内容隐藏的项目)。
答案 1 :(得分: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;
}
}
如果列表变大,那么您可以向集合中添加足够的项目以填补空白并重新运行该过程。
答案 2 :(得分:-2)
可以在此隐含地找到解决方案: Solution