Проблема создания ObservableCollection и ListBoxItem DataTemplate

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

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

Пока что.

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


person Kornelije Petak    schedule 08.05.2009    source источник


Ответы (3)


На самом деле, я пытался заставить это работать, и я нашел функцию ".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;
    }

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

person Community    schedule 05.08.2009

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

person pjbelf    schedule 08.05.2009
comment
Нет, вызов Measure () не устранил проблему. Думаю, мне следует воздержаться от потока пользовательского интерфейса, но я не знаю, какую часть вызывать в потоке пользовательского интерфейса и как. - person Kornelije Petak; 08.05.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;
    }
person Kornelije Petak    schedule 08.05.2009