【问题标题】:Calculating number of visible items in ListBox计算 ListBox 中可见项目的数量
【发布时间】:2010-10-24 05:39:55
【问题描述】:

我有一门课叫书;

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 中)。

这样做的问题是我希望显示的书籍数量足够小,以便列表框不显示滚动条,所以我可以实现自己的滚动方法(First、Previous、Next、Last,或者只是我自己的滚动条,没关系)。

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

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

有什么方法可以实现我想要做的吗?


编辑(作为对 Martin Harris 的回复)

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

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


编辑

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

【问题讨论】:

    标签: wpf listbox scroll visible


    【解决方案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. 在表单文本栏上显示列表框行数,基于列表框高度除以每个项目的大小。

    就是这样。

    【讨论】:

      【解决方案2】:

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

      我从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);
      }
      

      之后,您可以遍历列表框项并使用该测试来确定哪些是可见的。此列表的计数将为您提供列表框中可见项目的数量。

      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;
      }
      

      这将包含当前显示在列表框中的项目列表(包括那些通过滚动或类似方式隐藏的项目)。

      【讨论】:

        【解决方案3】:

        可以在此处隐式找到解决方案: Solution

        【讨论】:

          【解决方案4】:

          您是否可能在将所有书籍添加到列表框之前找出它们的大小(可能通过在后面的代码中解析和填充模板 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;
              }
          }
          

          如果列表变大,那么您可以向集合中添加足够的项目以填补空白并重新运行该过程。

          【讨论】:

          • 一个项目的高度是从 20 到 60 像素...所以当最大化时,列表框可​​能(在 1280*1024 - 我的分辨率)在 800 左右 / 导致大约 40-50项目。这可能有效,但问题是用户可以选择一个项目,然后使用键盘向下滚动到那些不可见的项目。这是不受欢迎的情况。
          猜你喜欢
          • 2019-07-29
          • 1970-01-01
          • 1970-01-01
          • 2011-09-03
          • 1970-01-01
          • 2021-12-27
          • 2015-08-18
          • 2017-12-09
          • 2012-06-13
          相关资源
          最近更新 更多