ObservableCollection和ListBoxItem DataTemplate生成问题

时间:2009-05-08 14:48:59

标签: c# wpf observablecollection listboxitem

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不为null,CalculateItemsHeight在所有for循环迭代中返回0)似乎没有创建datatemplate中定义的UI元素...

然而

现在,如果我在_display.Add(经文)之后添加了一些MessageBox,你可以看到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显然会立即创建一个项目,但不会创建它的可视元素(但是之后会添加它们,我只是不确切知道何时)。如何在不必弹出消息框的情况下执行相同的操作?

3 个答案:

答案 0 :(得分: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;
    }

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

希望这有帮助!

答案 1 :(得分:0)

wpf布局引擎不会通过布局并安排传递,因此您的listboxitems还没有给出大小。粘贴在消息框中将允许执行此操作的后台线程运行。在查看尺寸之前,请尝试强制调用Measure()。

答案 2 :(得分:0)

<强>解决

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

关键是在检索项目高度之前刷新项目的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;
    }