2016-06-29 37 views
6

Sto provando a migliorare le prestazioni con la mia applicazione WPF e sto avendo problemi con un ItemsControl complesso. Anche se ho aggiunto la virtualizzazione, c'è ancora un problema di prestazioni e penso di aver capito perché.C'è un modo di utilizzare la virtualizzazione con pannelli nascosti o espansioni?

Ogni articolo contiene una serie di aree espandibili. L'utente vede quindi un riepilogo all'inizio ma può eseguire il drill down espandendo per visualizzare ulteriori informazioni. Ecco come appare:

enter image description here

Come si può vedere, ci sono alcune ItemsControls nidificate. Quindi ognuno degli elementi di livello superiore ha un gruppo di controlli nascosti. La virtualizzazione impedisce il caricamento degli elementi fuori schermo, ma non gli oggetti nascosti all'interno degli elementi stessi. Di conseguenza, il layout iniziale relativamente semplice richiede molto tempo. Sfogliando alcune di queste visualizzazioni, l'87% del tempo è dedicato all'analisi e al layout e sono necessari alcuni secondi per caricarsi.

Preferisco che sia necessario 200 ms per espanderlo quando (se!) Decide l'utente, anziché 2 secondi per caricare la pagina nel suo complesso.

Chiedere consiglio davvero. Non riesco a pensare ad un buon modo di aggiungere i controlli usando MVVM comunque. Esiste un expander o virtualizzazione basata sulla visibilità supportata in WPF o dovrei creare la mia implementazione?

La figura 87% proviene da diagnostica:

enter image description here

+1

Cosa sono * "oggetti nascosti all'interno degli articoli stessi" *? La virtualizzazione non creerà ['ContentPresenter'] (http://stackoverflow.com/a/12437064/1997232), ma da qualche parte hai' ObservableCollection <> 'che deve essere completamente caricato. Quindi, cosa include questo "2s"? Avete fatto il profiling per vedere cosa è in realtà un collo di bottiglia? – Sinatr

+0

Vuoi virtualizzare parte dell'elemento che si trova all'interno di "Expander" (per gli oggetti creati)? In caso affermativo, puoi gestire lo stato compresso in ViewModel per ** aggiungere più dati ** quando è espanso (ad esempio usando 'object' per contenere dati e modelli dati per creare elementi visivi quando non è' null'). – Sinatr

+0

Ecco come funziona il layout. È possibile ritardare l'acquisizione dei dati finché non è aperto. Sono sorpreso che impiega 2 secondi. – Paparazzi

risposta

5

Se avete semplicemente

- Expander 
     Container 
      some bindings 
    - Expander 
      Container 
       some bindings 
+ Expander 
+ Expander 
... invisible items 

Allora sì, Container e tutti gli attacchi vengono inizializzate nel momento in cui viene visualizzato vista (e ItemsControl crea ContentPresenter per gli elementi visibili).

Se si desidera virtualizzare contenuto di Expander quando è crollato, quindi è possibile utilizzare i dati-template

public ObservableCollection<Item> Items = ... // bind ItemsControl.ItemsSource to this 

class Item : INotifyPropertyChanged 
{ 
    bool _isExpanded; 
    public bool IsExpanded // bind Expander.IsExpanded to this 
    { 
     get { return _isExpanded; } 
     set 
     { 
      Data = value ? new SubItem(this) : null; 
      OnPropertyChanged(nameof(Data)); 
     } 
    } 

    public object Data {get; private set;} // bind item Content to this 
} 

public SubItem: INotifyPropertyChanged { ... } 

Spero che non c'è bisogno di spiegare come fare data-template di SubItem in XAML.

Se lo si fa, quindi inizialmente Data == null e non viene caricato nulla eccetto Expander. Non appena viene espanso (dall'utente o programmaticamente) la vista creerà immagini.

+0

L'uso dei modelli è una grande idea, l'ho implementato più o meno come descrivi e le prestazioni sono molto migliorate. Grazie! – Joe

1

Ho pensato di inserire i dettagli della soluzione, che è praticamente un'implementazione diretta della risposta di Sinatr.

Ho utilizzato un controllo del contenuto, con un selettore di modelli di dati molto semplice. Il selettore modello controlla semplicemente se l'elemento di contenuto è nullo, e sceglie tra due modelli di dati:

public class VirtualizationNullTemplateSelector : DataTemplateSelector 
{ 
    public DataTemplate NullTemplate { get; set; } 
    public DataTemplate Template { get; set; } 

    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     if (item == null) 
     { 
      return NullTemplate; 
     } 
     else 
     { 
      return Template; 

     } 
    } 
} 

La ragione di questo è che l'ho usato ContentControl stabilisce ancora il modello di dati, anche se il contenuto è nullo.Così mi sono messo questi due modelli nella XAML:

  <ContentControl Content="{Binding VirtualizedViewModel}" Grid.Row="1" Grid.ColumnSpan="2" ><!--Visibility="{Binding Expanded}"--> 
       <ContentControl.Resources> 
        <DataTemplate x:Key="Template"> 
         <StackPanel> 
          ...complex layout that isn't often seen... 
         </StackPanel> 
        </DataTemplate> 
        <DataTemplate x:Key="NullTemplate"/> 
       </ContentControl.Resources> 
       <ContentControl.ContentTemplateSelector> 
        <Helpers:VirtualizationNullTemplateSelector Template="{StaticResource Template}" NullTemplate="{StaticResource NullTemplate}"/> 
       </ContentControl.ContentTemplateSelector> 
      </ContentControl> 

Infine, invece di usare una nuova classe intera per un sub-elemento, è abbastanza semplice per creare un oggetto "VirtualizedViewModel" nel vostro modello di vista che fa riferimento a "questo ":

private bool expanded; 
    public bool Expanded 
    { 
     get { return expanded; } 
     set 
     { 
      if (expanded != value) 
      { 
       expanded = value; 
       NotifyOfPropertyChange(() => VirtualizedViewModel); 
       NotifyOfPropertyChange(() => Expanded); 
      } 
     } 
    } 


    public MyViewModel VirtualizedViewModel 
    { 
     get 
     { 
      if (Expanded) 
      { 
       return this; 
      } 
      else 
      { 
       return null; 
      } 
     } 
    } 

Ho ridotto il tempo di caricamento di 2-3 secondi di circa il 75% e ora sembra molto più ragionevole.