【问题标题】:ObservableCollection and ListBoxItem DataTemplate generation problemObservableCollection 和 ListBoxItem DataTemplate 生成问题
【发布时间】:2009-05-08 14:48:58
【问题描述】:

ObservableCollection 发生了一些奇怪的事情。

我有以下代码:

private readonly ObservableCollection<DisplayVerse> _display;
private readonly ListBox _box;

    private void TransferToDisplay()
    {
        double elementsHeight = 0;

        _display.Clear();

        for (int i = 0; i < _source.Count; i++) {
            DisplayVerse verse = _source[i];
            _display.Add(verse);
            elementsHeight += CalculateItemsHeight(i);
            if (elementsHeight + Offset > _box.ActualHeight) {
                _display.RemoveAt(_display.Count - 1);
                break;
            }
        }
        MessageBox.Show(elementsHeight.ToString());
    }

    private double CalculateItemsHeight(int index)
    {
        ListBoxItem lbi = _box.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
        return lbi != null ? lbi.ActualHeight : 0;
    }

我在这里要做的是控制有多少项目进入 ObservableCollection _display。现在,在这个 for 循环中,您可以看到元素被添加,直到总元素高度(+offset)大于列表框本身。

现在,这很奇怪,在这个 for 循环之后 elementsHeight 等于 0。 (即使 lbi 不为空,CalculateItemsHeight 在所有 for 循环迭代中都返回 0)似乎未创建数据模板中定义的 UI 元素...

还是。

现在,如果我在 _display.Add(verse) 之后放置一些 MessageBoxes,您可以看到 CalculateItemsHeight 实际上返回项目的高度。

for (int i = 0; i < _source.Count; i++) {
    DisplayVerse verse = _source[i];
    _display.Add(verse);
    MessageBox.Show("pause"); // <----- PROBLEM?
    elementsHeight += CalculateItemsHeight(i);
    if (elementsHeight + Offset > _box.ActualHeight) {
        _display.RemoveAt(_display.Count - 1);
        break;
    }
}
MessageBox.Show(elementsHeight.ToString());

如图所示修改for循环后,最后一个MessageBox实际上显示所有已处理元素的实际高度。

我的问题是 - UI 元素实际创建时间是什么时候?似乎它是在 MessageBox 显示期间的某个地方完成的。这种行为对我来说很奇怪,可能与线程有关,不确定。

添加到 _display ObservableCollection 显然会立即创建一个项目,而不是它的视觉元素(但是它们是在之后添加的,我只是不知道确切的时间)。如何在不弹出消息框的情况下执行相同的操作?

【问题讨论】:

    标签: c# wpf observablecollection listboxitem


    【解决方案1】:

    实际上,我试图让它工作,我发现“.UpdateLayout()”函数对我来说非常有效。我意识到你在做垂直,我在做水平,但这是我的代码,很简单:

    for (int i = 0; i < listOfItems.ItemsIn.Count; ++i)
        {
            //CalculateItemsHeight(i);
    
            ListBoxItem abc = (lb.ItemContainerGenerator.ContainerFromItem(lb.Items[i]) as ListBoxItem);
            abc.UpdateLayout();
            totalWidth += abc.ActualWidth;
        }
    

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      wpf 布局引擎不会通过布局和排列通道,因此您的列表框项还不会被指定大小。粘贴在消息框中将允许执行此操作的后台线程运行。在查看它们的大小之前,尝试在你的项目上强制调用 Measure()。

      【讨论】:

      • 不,调用 Measure() 并没有解决问题。我想我应该对 UI 线程进行调整,但我不知道在 UI 线程上调用哪个部分或如何调用。
      【解决方案3】:

      已解决

      这会在几分之一秒内产生一些闪烁效果(就像一个一个地加载项目一样),但实际上符合我的需要。

      重点是在检索项目高度之前刷新项目的 UI。

      我创建了一个扩展方法:

          public static void RefreshUI(this DependencyObject obj)
          {
              obj.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Loaded, (Action)delegate { });
          }
      

      然后在检索高度之前,我刷新了 UI。

      private double CalculateItemsHeight(int index)
          {
              ListBoxItem lbi = _box.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
              if (lbi != null) {
                  lbi.RefreshUI();
                  return lbi.ActualHeight;
              }
              return 0;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-02
        • 2017-07-17
        • 2010-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-12
        • 2011-07-04
        相关资源
        最近更新 更多