Проблема генерации ObservableCollection и ListBoxItem DataTemplate - PullRequest
2 голосов
/ 08 мая 2009

Что-то странное происходит с 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 вы можете видеть, что элементы добавляются до тех пор, пока общая высота элементов (+ смещение) не станет больше самого списка.

Теперь, это странно, elementsHeight равен 0 после этого цикла for. (CalculateItemsHeight возвращает 0 для всех итераций цикла, даже если значение lbi не равно нулю) Кажется, что элементы пользовательского интерфейса, определенные в шаблоне данных, не создаются ...

Тем не менее.

Теперь, если я добавлю несколько MessageBoxes после _display.Add (verse), вы увидите, что 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 фактически показывает фактическую высоту для всех обработанных элементов.

Мой вопрос - когда элементы пользовательского интерфейса действительно создаются ? Кажется, что это было сделано где-то во время отображения MessageBox. Такое поведение довольно странно для меня, может быть, это как-то связано с многопоточностью, не уверен.

При добавлении в _display ObservableCollection, очевидно, сразу создает элемент, но не его визуальные элементы (однако они добавляются впоследствии, я просто не знаю точно, когда). Как я могу сделать то же самое поведение без всплывающего окна сообщения?

Ответы [ 3 ]

1 голос
/ 06 августа 2009

На самом деле, я пытался заставить это работать, и я нашел функцию ".UpdateLayout ()", которая отлично работает для меня. Я понимаю, что вы делаете вертикальный, а я делаю горизонтальный, но вот мой код, он довольно прост:

</p> <pre><code>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; }

Надеюсь, это поможет!

0 голосов
/ 09 мая 2009

решаемые

Это создает несколько мерцающий эффект в течение доли секунды (как будто загрузка элементов по одному), но на самом деле соответствует моим потребностям.

Суть в том, чтобы обновить пользовательский интерфейс для элемента перед получением его высоты.

Я создал метод расширения:

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

А затем, прежде чем получить высоту, я обновляю интерфейс.

private double CalculateItemsHeight(int index)
    {
        ListBoxItem lbi = _box.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
        if (lbi != null) {
            lbi.RefreshUI();
            return lbi.ActualHeight;
        }
        return 0;
    }
0 голосов
/ 08 мая 2009

Механизм компоновки wpf не прошел через макет и не упорядочил проход, поэтому вашим элементам списка еще не был задан размер. Застревание в окне сообщения позволит фоновым потокам, которые делают это, запускаться. Попробуйте принудительно вызвать Measure () для ваших элементов, прежде чем смотреть на их размер.

...