WPF DataGrid: свойство CanContentScroll, вызывающее странное поведение
У меня есть решение, в котором я генерирую DataGrid (или несколько экземпляров) на основе пользовательских критериев.. каждая сетка продолжает получать данные, как они поступают через ObservableCollection
проблема у меня была в том, что свиток действовал странно. Это было изменчиво, и полоса прокрутки изменила бы размер самостоятельно, прокручивая.
чем я нашел.. Свойство CanContentScroll! Это полностью исправляет странное поведение прокрутки, приносящее мне временное счастье и счастье.
однако, это вызывает 2 неблагоприятных побочных эффекта.
всякий раз, когда я воссоздаю экземпляры сетки и связываю их с моей наблюдаемой коллекцией, она замораживает все мое окно на 5 секунд. когда моя сетка становится больше, эта задержка может длиться 30 секунд.
когда я вызываю TradeGrid.ScrollIntoView(TradeGrid.Items(TradeGrid.Items.Count - 1)), чтобы прокрутить до дна, он переходит к основанию и затем возвращается к вершине.
Есть ли другой способ добиться плавной прокрутки, возможно?
2 ответа
Вы сталкиваетесь с различиями между физической прокруткой и логической прокруткой.
Как вы обнаружили, у каждого есть свои компромиссы.
Физическая прокрутка
Физическая прокрутка (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) за счет некоторого очень сложного кода.
Использование другой панели потребует гораздо больших усилий: VirtualizingStackPanel - единственная панель, которая виртуализирует, и только она и StackPanel для логической прокрутки. Поскольку вы используете возможности виртуализации VirtualizingStackPanel, вам придется заново реализовать все эти функции, а также все информационные функции IScrollInfo и обычные функции Panel. Я мог бы сделать что-то подобное, но я бы выделил несколько, возможно, много дней, чтобы сделать это правильно. Я рекомендую вам не пробовать это.
У меня также есть та же проблема с моей DataGrid, и, наконец, я сделал:
ScrollViewer.CanContentScroll="True"
EnableRowVirtualization="True"
VirtualizingPanel.VirtualizationMode="Standard"
Теперь все отлично работает в моей DataGrid.