2012-12-14 7 views
12

Problema: La visualizzazione di grandi quantità di dati in un'area scorrevole presenta prestazioni orribili e/o esperienza utente.Problema di prestazioni della virtualizzazione con ampi dati a scorrimento SL4

Provato: Fondamentalmente impostato un DataTemplate in un ListBox per mostrare una griglia di dati popolati con il VirtualizationMode impostato su Ricicla e un'altezza fissa impostata sul ListBox stesso. Qualcosa come l'esempio qui sotto.

<ListBox x:Name="Items" 
     TabNavigation="Once" 
     VirtualizingStackPanel.VirtualizationMode="Recycling"  
     Height="500">   
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <StackPanel Orientation="Horizontal" Margin="0,5"> 
         <HyperlinkButton Content="Action" Margin="5"/> 
         <ContentControl 
           cal:View.Model="{Binding}" 
           VerticalContentAlignment="Stretch" 
           HorizontalContentAlignment="Stretch"/> 
        </StackPanel> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 

Il ContentControl avrebbe portato in uno standard <Grid> da un'altra vista che formatta il layout complessivo degli elementi popolate costituiti TextBlocks circa 20 statico, e 20 dati associati.

Funziona bene e taglia i carichi iniziali a metà. TUTTAVIA, ora il problema è che ho bisogno che l'altezza NON sia una dimensione fissa, quindi occupa lo spazio disponibile nel suo genitore e può anche essere ridimensionata. Grazie a @DanFox ho scoperto che devi correggere l'altezza in un modo o nell'altro per invocare la virtualizzazione o il RenderEngine pensa che abbia comunque una stanza infinita.

La domanda è: C'è un modo migliore per farlo, o come posso almeno correggere la tecnica corrente per consentire una migliore UX? Sto generando potenzialmente centinaia di questi elementi quindi ho bisogno del miglioramento delle prestazioni della virtualizzazione. Tuttavia, devo anche consentire all'utente di ridimensionare la finestra e mantenere la capacità di scorrere in modo efficace.

Qualsiasi intuizione è molto apprezzata, grazie e Buone Feste!

+0

Hai provato a fissare temporaneamente l'altezza di ScrollViewer e altri componenti? A volte il calo delle prestazioni può essere dovuto al fatto che il motore di layout sta dando un'altezza infinita a ScrollViewer. Scusate, non posso essere più esplicito, sono un po 'arrugginito in quest'area, non ho più fatto SL per un po' di tempo ... –

+0

I dati si caricano rapidamente? A quale tipo di collezione sei legato? –

+0

@Dan Fox Quindi, solo il difficile aggiustamento dell'altezza dello scrollviewer potrebbe influire sulla velocità di rendering? –

risposta

1

Ok quindi ecco cosa ho finito e devo dire che è un enorme miglioramento! Abbiamo iniziato con un tempo di caricamento di 77 secondi sul punto per questo particolare pezzo. Con alcuni refactoring su come eravamo vincolanti sul retro ci siamo sbarazzati di circa 20 secondi, quindi il problema era ancora nel rendering dell'interfaccia utente. La risposta qui sotto è ovviamente più semplice di quanto pensassi all'inizio e ora stiamo caricando enormi quantità di dati in 15-20 secondi (con la virtualizzazione ovviamente) e carichi più piccoli sono fondamentalmente istantanei e ci rimettono in carreggiata.

Ecco cosa ho fatto;

<!-- This needs to be contained in a parent panel like a grid --> 
<ListBox x:Name="Items" > 
       <ListBox.Template> 
        <ControlTemplate> <!-- Broke it out to allow resizing --> 
         <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 
          <ItemsPresenter/> <!-- This little fella does magical things -->    
         </ScrollViewer>   
        </ControlTemplate>  
       </ListBox.Template> 
       <ListBox.ItemsPanel> 
        <ItemsPanelTemplate> 
         <VirtualizingStackPanel VirtualizingStackPanel.VirtualizationMode="Recycling"/> <!-- Recycle was a must -->   
        </ItemsPanelTemplate>  
       </ListBox.ItemsPanel> 
       <ListBox.ItemTemplate> 
        <DataTemplate> 
         <StackPanel Orientation="Horizontal"> 
          <HyperlinkButton VerticalAlignment="Top" Margin="5" /> 
           <!-- This guy I did need to set a minwidth on to retain nice and predictable scrolling 
when datacontext was potentially changing --> 
          <ContentControl cal:View.Model="{Binding}" MinWidth="1160" 
              VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" /> 
         </StackPanel> 
        </DataTemplate>   
       </ListBox.ItemTemplate>   
      </ListBox> 

E questo è tutto! Ecco! È bastato rompere il ControlTemplate e applicare un ItemPresenter diretto! Ora virtualizza alla grande, ridimensiona, l'equilibrio è stato ripristinato nell'universo e non voglio più dare un pugno a un unicorno. Dopodiché ho appena rimosso gli stati visivi perché non avevamo bisogno di alcun tipo di selezione degli oggetti e questo è tutto, grazie per l'intuizione di tutti! Mi spiace di non poter assegnare una taglia questa volta.

1

come richiesto :-) non mi sentivo come se fossi "risposto" niente finora ...

Hai assolutamente ragione, ci dovrebbe essere un modo di ottenere l'altezza dinamica. Impostarlo come altezza fissa è un controllo rapido solo per assicurarsi che la virtualizzazione funzioni. Sei arrivato ovunque con il profiler delle prestazioni, o magari usando SilverlightSpy? Ho risolto uno di questi problemi rifattorizzando il binding sull'interfaccia utente/macchina virtuale per renderlo più efficiente. C'era una buona risorsa per WinPhone7 SL su questo con una soluzione potenzialmente buona, vedrò se riesco a scavare (la teoria dovrebbe trasferirmi su SL full-fat)

This ha un buon punto sul caching a seconda di l'architettura VM

This potrebbe essere utile in quanto spiega Laxy caricando un po 'più

E un couple of hints dal team di sviluppo WinPho 7.

Speranza che aiuta

0

Se la sua solo per l'altezza fissa, perchè non si cercano solo questo:

<Grid>  
<ListBox x:Name="Items" 
     TabNavigation="Once" 
     VirtualizingStackPanel.VirtualizationMode="Recycling"  
     Height="{Binding Path=Height,RelativeSource={RelativeSource AncestorType=Grid}}">   
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <StackPanel Orientation="Horizontal" Margin="0,5"> 
         <HyperlinkButton Content="Action" Margin="5"/> 
         <ContentControl 
           cal:View.Model="{Binding}" 
           VerticalContentAlignment="Stretch" 
           HorizontalContentAlignment="Stretch"/> 
        </StackPanel> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 
</Grid> 

importante che la ListBox in un contenitore che si adatta allo spazio esterno e di non lo spazio interno come un pannello di sostegno.

Prova questo e dimmi se aiuta. :)

+0

Potrei sbagliarmi, ma non penso che tu possa usare RelativeSource come questo in SL4? Penso che sia stato migliorato in SL5? –

+0

Dan's correct, SL4 fa solo Self e TemplatedParent, AncestorType è un no go che è un peccato perché mi imbatto in istanze dove potrei usarlo tutto il tempo. Sulla stessa strada ho provato comunque Height = {Binding ActualHeight, ElementName = ParentGridContainter} e non ho avuto fortuna. Ho associato un blocco di testo alla proprietà e restituisce un numero completamente non valido ... ma questo è un altro argomento lol. –

+0

Sì, mi dispiace AncestorType è nuovo in SL5. Prova a legare in altezza e non in ActualHeight –

0

Penso che se stai magicamente inserendo i controlli dell'interfaccia utente nel tuo datatemplate, stai vanificando lo scopo della virtualizzazione. Questi controlli (griglie) sono essi stessi riutilizzati? Cosa succede quando cambia {Binding}?Quanto codice viene eseguito in quello che sembra un comportamento collegato? Forse quello che hai fatto potrebbe essere reso performante. Ma se si crea un datatemplate straight-forward (o si inserisce un normale UserControl), si saprà che tutti i controlli in esso contenuti verranno riutilizzati e che verrà applicata una penalità minima per il passaggio del proprio datacontext.

Non sono sicuro del problema con Altezza. Dovrebbe funzionare (avere un'altezza finita in Arrange), ma dipende da cosa sta facendo sotto il cofano (forse sta cercando di capire tutto prima che Arrange renda l'altezza disponibile).

È sempre possibile creare il proprio controllo degli oggetti di virtualizzazione. Ricavare dal Pannello, inserirlo in un ScrollViewer e implementare IScrollInfo sul Pannello. Allora saprai TUTTI i motivi per cui le cose sono come sono :)

+0

Puoi anche provare a usare VirtualizingStackPanel direttamente all'interno di un ScrollViewer. Questo potrebbe essere un modo migliore per forzare la virtualizzazione. Puoi anche derivare da VirtualizingStackPanel, che può dare i vantaggi di derivare da Panel ma con meno lavoro. –

0

Non sono sicuro, se questa sarebbe una soluzione accettabile per il tuo caso d'uso, ma ho creato un ContentControl denominato DelayedLayoutUpdateContainer che include un layout complesso e aiuta a migliorare le prestazioni. L'idea alla base di DelayedLayoutUpdateContainer è che aggiusta la dimensione del suo contenuto solo quando non è stato ridimensionato per un determinato intervallo di tempo. La dimensione di ContentPresenter all'interno di ControlTemplate è impostata su valori assoluti. In modo che potrebbe avere lo stesso effetto di impostare l'altezza ListBoxes su un valore assoluto.

Problemi correlati