WPF DataGrid: propriedade CanContentScroll causando comportamento estranho

Eu tenho uma solução onde eu gerar um DataGrid (ou várias instâncias) com base em critérios do usuário .. cada grade continua recebendo dados como ele vem via ObservableCollection

o problema que eu tive, foi que o pergaminho agiu estranho. Era agitado e a barra de rolagem se redimensionava durante a rolagem.

do que eu encontrei .. CanContentScroll propriedade! Ele corrige completamente o estranho comportamento de rolagem, trazendo-me felicidade temporária e felicidade.

no entanto, causa 2 efeitos colaterais infelizes.

  1. sempre que eu recriar instâncias de grade e vinculá-las à minha coleção observável, ela congela minha janela inteira por 5 segundos. quando minha grade cresce para um tamanho grande, esse atraso pode durar 30 segundos.

  2. quando eu chamo TradeGrid.ScrollIntoView (TradeGrid.Items (TradeGrid.Items.Count – 1)) para rolar para a parte inferior, ele salta para baixo e de volta para o início.

Existe outra maneira de obter rolagem suave, talvez?

Você está encontrando as diferenças entre a rolagem física e a rolagem lógica .

Como você descobriu, cada um tem suas compensações.

Rolagem física

Rolagem física (CanContentScroll = false) apenas passa por pixels, portanto:

  • A janela de exibição sempre representa exatamente a mesma parte da sua extensão de rolagem, oferecendo uma experiência de rolagem suave e

mas

  • Todo o conteúdo do DataGrid deve ter todos os modelos totalmente aplicados e ser medido e organizado para determinar o tamanho da barra de rolagem, levando a longos atrasos durante o carregamento e alto uso de RAM, e
  • Ele realmente não rola itens, por isso não entende ScrollIntoView muito bem

Rolagem lógica

A rolagem lógica (CanContentScroll = true) calcula sua viewport de rolagem e extensão por itens em vez de pixels, portanto:

  • A janela de visualização pode mostrar um número diferente de itens em momentos diferentes, o que significa que o número de itens na viewport em comparação com o número de itens na extensão varia, causando a alteração do comprimento da barra de rolagem e

  • Rolagem se move de um item para o outro e nunca entre, levando a rolagem “irregular”

mas

  • Contanto que você esteja usando o VirtualizingStackPanel, ele só precisa aplicar modelos e medir e organizar os itens que estão realmente visíveis no momento.

  • ScrollIntoView é muito mais simples, pois só precisa exibir o índice do item correto

Escolhendo entre eles

Esses são os dois únicos tipos de rolagem fornecidos pelo WPF. Você deve escolher entre eles com base nos tradeoffs acima. Geralmente, a rolagem lógica é melhor para conjuntos de dados médios a grandes, e a rolagem física é melhor para os pequenos.

Um truque para acelerar o carregamento durante a rolagem física é tornar a rolagem física melhor para envolver seus itens em um Decorator personalizado que tenha um tamanho fixo e defina a visibilidade de seu filho como oculto quando ele não estiver visível. Isso evita que ApplyTemplate, Measure e Arrange ocorram nos controles descendentes desse item até que você esteja pronto para que isso aconteça.

Um truque para tornar ScrollIntoView da rolagem física mais confiável é chamá-lo duas vezes: uma vez imediatamente e uma vez em um callback de dispatcher de DispatcherPriority.ApplicationIdle.

Tornando a barra de rolagem de rolagem lógica mais estável

Se todos os seus itens tiverem a mesma altura, o número de itens visíveis na janela de visualização a qualquer momento permanecerá o mesmo, fazendo com que o tamanho do polegar de rolagem permaneça o mesmo (porque a proporção com o número total, se os itens não mudarem).

Também é possível modificar o comportamento da própria ScrollBar para que o polegar seja sempre calculado para ter um tamanho fixo. Para fazer isso sem qualquer hacky code-behind:

  • Subclass Track para replace o cálculo da posição e do tamanho do polegar em MeasureOverride com o seu próprio
  • Alterar o modelo da barra de rolagem usado para a barra de rolagem de rolagem lógica para usar sua faixa com subclass em vez da faixa normal
  • Alterar o modelo ScrollViewer para definir explicitamente seu modelo ScrollBar personalizado na ScrollBar de rolagem lógica (em vez de usar o modelo padrão)
  • Alterar o modelo de checkbox de listview para usar explicitamente definir seu modelo ScrollViewer personalizado no ScrollViewer cria

Isso significa copiar vários códigos de modelo dos modelos WPF internos, por isso não é uma solução muito elegante. Mas a alternativa para isso é usar hacky code-behind para esperar até que todos os templates sejam expandidos, então encontre o ScrollBar e apenas substitua o template ScrollBar pelo que usa o seu Track personalizado. Este código salva dois modelos grandes (ListBox, ScrollViewer) ao custo de um código muito complicado.

Usando um painel diferente seria uma quantidade muito maior de trabalho: VirtualizingStackPanel é o único painel que virtualiza e apenas ele e StackPanel para rolagem lógica. Como você está aproveitando as virtualizações do VirtualizingStackPanel, você terá que reimplementá-las, além de todas as funções de informações do IScrollInfo, além das funções normais do Painel. Eu poderia fazer algo assim, mas eu iria alocar vários, talvez muitos dias para acertar. Eu recomendo que você não tente.

Eu também tenho o mesmo problema com o meu DataGrid e finalmente fiz:

ScrollViewer.CanContentScroll="True" EnableRowVirtualization="True" VirtualizingPanel.VirtualizationMode="Standard" 

Agora tudo está funcionando bem no meu DataGrid.