Вы сталкиваетесь с различиями между физической прокруткой и логической прокруткой.
Как вы обнаружили, у каждого есть свои компромиссы.
Физическая прокрутка
Физическая прокрутка (CanContentScroll = false) просто идет по пикселям, поэтому:
- Окно просмотра всегда представляет одну и ту же часть вашего экстента прокрутки, обеспечивая плавную прокрутку, и
но
- Для всего содержимого DataGrid должны быть полностью применены все шаблоны, и они должны быть измерены и упорядочены для определения размера полосы прокрутки, что приводит к длительным задержкам во время загрузки и высокому использованию ОЗУ, а также
- Он не прокручивает элементы, поэтому не очень хорошо понимает ScrollIntoView
Логическая прокрутка
Логическая прокрутка (CanContentScroll = true) вычисляет область просмотра и экстент прокрутки по элементам вместо пикселей, поэтому:
В окне просмотра может отображаться различное количество элементов в разное время, что означает количество элементов в окне просмотра по сравнению с количеством элементов в экстенте, что приводит к изменению длины полосы прокрутки и
Прокрутка перемещается от одного элемента к другому и никогда между ними, что приводит к "рывковой" прокрутке
но
Пока вы используете VirtualizingStackPanel изнутри, нужно только применять шаблоны, измерять и упорядочивать элементы, которые на самом деле видны в данный момент, и
ScrollIntoView намного проще, поскольку ему просто нужно получить правильный индекс элемента в представлении
Выбор между ними
Это только два вида прокрутки, предоставляемые WPF. Вы должны выбрать между ними на основе вышеуказанных компромиссов. Обычно логическая прокрутка подходит для средних и больших наборов данных, а физическая прокрутка - для небольших.
Хитрость для ускорения загрузки во время физической прокрутки состоит в том, чтобы улучшить физическую прокрутку - это обернуть ваши элементы в пользовательском декораторе, который имеет фиксированный размер и устанавливает для видимости своего ребенка значение Скрытый, когда он не виден. Это предотвращает возникновение ApplyTemplate, Measure and Arrange на дочерних элементах управления этого элемента, пока вы не будете готовы к этому.
Хитрость для повышения надежности ScrollIntoView физической прокрутки состоит в том, чтобы вызывать его дважды: один раз немедленно и один раз при вызове диспетчера DispatcherPriority.ApplicationIdle.
Повышение устойчивости логической полосы прокрутки
Если все ваши элементы имеют одинаковую высоту, количество элементов, видимых в области просмотра в любое время, останется неизменным, в результате чего размер большого пальца прокрутки останется неизменным (потому что соотношение с общим числом, если элементы не изменяются ).
Также возможно изменить поведение самой полосы прокрутки, чтобы большой палец всегда вычислялся как фиксированный размер. Чтобы сделать это без какого-либо хакерского кода:
- Трек подкласса для замены расчета положения и размера большого пальца в MeasureOverride на собственный
- Измените шаблон ScrollBar, используемый для ScrollBar с логической прокруткой, чтобы использовать ваш подклассный трек вместо обычного
- Измените шаблон ScrollViewer, чтобы явно настроить свой пользовательский шаблон ScrollBar на логической полосе прокрутки (вместо использования шаблона по умолчанию)
- Измените шаблон ListBox, чтобы использовать его для явной установки собственного шаблона ScrollViewer в создаваемом им ScrollViewer
Это означает копирование большого количества кода шаблона из встроенных шаблонов WPF, поэтому это не очень элегантное решение. Но альтернативой этому является использование хакерского кода для ожидания, пока все шаблоны не будут расширены, затем найдите ScrollBar и просто замените шаблон ScrollBar тем, который использует ваш собственный трек. Этот код сохраняет два больших шаблона (ListBox, ScrollViewer) за счет некоторого очень сложного кода.
Использование другой Panel было бы гораздо большим трудом: VirtualizingStackPanel - единственная панель, которая виртуализирует, и только она и StackPanel для логической прокрутки. Поскольку вы используете возможности виртуализации VirtualizingStackPanel, вам придется заново реализовать все эти функции, а также все информационные функции IScrollInfo и обычные функции Panel. Я мог бы сделать что-то подобное, но я бы выделил несколько, возможно, много дней, чтобы сделать это правильно. Я рекомендую вам не пробовать.