Нет очень многих опций для виртуализации, переносят панель для использования в WPF. По той или иной причине MS, решенный для не поставки один в стандартной библиотеке.
Если кто-либо мог бы быть столь полужирным, что предоставил бы исходный ответ толпы (и explaination) к первому объекту работы на следующем codeplex проекте, я буду значительно ценить его:
http://virtualwrappanel.codeplex.com/workitem/1
Спасибо!
Сводка проблемы:
Я недавно попытался использовать виртуализацию wrappanel из этого проекта и встретился с ошибкой.
Шаги для репродуцирования:
Отладка. Утверждайте сбои (Отладка. Утверждайте (ребенок == _children[childIndex], "Неправильный ребенок был сгенерирован") ;) в MeasureOverride, и длительные результаты выполнения в пустой исключительной ситуации в методе Очистки [видят присоединенный снимок экрана].
Сообщите мне, можете ли Вы исправить это.
Спасибо,
АО
Код:
http://virtualwrappanel.codeplex.com/SourceControl/list/changesets#
Метод OnItemsChanged должен правильно обрабатывать параметры args. См. Этот вопрос для получения дополнительной информации. Скопировав код из этого вопроса, вам нужно будет обновить OnItemsChanged следующим образом:
protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args) {
base.OnItemsChanged(sender, args);
_abstractPanel = null;
ResetScrollInfo();
// ...ADD THIS...
switch (args.Action) {
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Replace:
RemoveInternalChildRange(args.Position.Index, args.ItemUICount);
break;
case NotifyCollectionChangedAction.Move:
RemoveInternalChildRange(args.OldPosition.Index, args.ItemUICount);
break;
}
}
Во-первых, имейте в виду, что в целом, если вы удаляете объект из коллекции и у вас нет ссылки на него, это объект мертв на момент удаления. Таким образом, по крайней мере, вызов RemoveInternalChildRange является незаконным после удаления, но это не основная проблема.
Во-вторых, у вас может быть небольшое состояние гонки, даже если оно не является строго многопоточным. Необходимо проверить (с помощью точки останова), слишком ли активно реагирует этот обработчик событий - вы не хотите, чтобы обработчик событий работал, пока вы все еще находитесь в середине удаления, даже если это один элемент.
В-третьих, проверьте наличие null после:
UIElement child = _generator.GenerateNext(out newlyRealized) as UIElement;
и для первой попытки измените код, чтобы обеспечить плавный выход, что в данном случае означает плавное продолжение - необходимо использовать цикл for и приращения в цикле, чтобы иметь возможность выполнять продолжать вообще.
Также проверьте InternalChildren, когда вы видите это значение null, чтобы увидеть, дает ли этот путь доступа тот же результат, что и ваш _children (например, размер, внутренние данные, null в том же месте).
Если просто пропуск нулевого значения выживает (отображается без исключений), сразу после этого остановите его в отладчике и проверьте, были ли эти массивы / коллекции урегулированы (внутри нет нулей).
Также опубликуйте полностью компилируемый образец проекта, который дает репро (в виде zip-файла) где-нибудь - уменьшает случайные предположения и позволяет ppl просто построить / запустить и посмотреть.
Кстати о предположениях - проверьте, что делает ваша «наблюдаемая коллекция». Если вы удаляете элемент из коллекции, любой итератор / перечислитель из предыдущего состояния этой коллекции имеет право выдавать или выдавать нули и в пользовательском интерфейсе, который пытается быть слишком умным, имея устаревший итератор может легко произойти.
Объяснение проблемы
Вы просили объяснить, что происходит не так, а также дать инструкции, как это исправить. Пока никто не объяснил проблему. Я сделаю так.
В ListBox с VirtualizingWrapPanel есть пять отдельных структур данных, которые отслеживают элементы, каждая по-разному:
Когда элемент удаляется из ItemsSource, это удаление должно распространяться на все структуры данных. Вот как это работает:
Из-за этого коллекция InternalChildren не синхронизирована с другими четырьмя коллекциями, что приводит к возникшим ошибкам.
Решение проблемы
Чтобы устранить проблему, добавьте следующий код в любом месте метода OnItemsChanged VirtualizingWrapPanel:
switch(args.Action)
{
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Replace:
RemoveInternalChildRange(args.Position.Index, args.ItemUICount);
break;
case NotifyCollectionChangedAction.Move:
RemoveInternalChildRange(args.OldPosition.Index, args.ItemUICount);
break;
}
Это поддерживает синхронизацию коллекции InternalChildren с другими структурами данных.
Почему здесь не вызывается AddInternalChild / InsertInternalChild
Вы можете задаться вопросом, почему в приведенном выше коде нет вызовов InsertInternalChild или AddInternalChild, и особенно почему обработка Replace и Move не требует от нас добавления нового элемента во время OnItemsChanged .
Ключ к пониманию этого - способ работы ItemContainerGenerator.
Когда ItemContainerGenerator получает событие удаления, он обрабатывает все немедленно:
С другой стороны, ItemContainerGenerator узнает, что элемент добавлен, все обычно откладывается:
Таким образом, все удаления из коллекции InternalChildren (включая те, которые являются частью Move или Replace) должны выполняться внутри OnItemsChanged, но добавления могут (и должны) быть отложены до следующего MeasureOverride.