2012-12-14 9 views
5

Ho una vista nella mia applicazione con enormi dati simili a tabelle da visualizzare. I dati vengono visualizzati in due UniformGrid nidificati. I UniformGrids sono ItemPanel in ItemsControls e sono associati ad alcuni ViewModels. Vedere la seguente immagine e alcuni esempi XAML-Code:Come gestire le prestazioni in enormi UniformGrids?

view and viewmodel http://img593.imageshack.us/img593/8825/stackoverflowuniformgri.png

<!-- The green boxes --> 
<ItemsControl ItemsSource="{Binding BigCells}"> 

    <ItemsControl.ItemPanel> 
    <PanelTemplate> 
     <UniformGrid /> 
    </PanelTemplate> 
    </ItemsControl.ItemPanel> 

    <ItemsControl.ItemTemplate> 
    <DataTemplate> 

     <!-- The blue boxes --> 
     <ItemsControl ItemsSource="{Binding SmallCells}"> 
     <ItemsControl.ItemPanel> 
      <PanelTemplate> 
      <UniformGrid /> 
      </PanelTemplate> 
     </ItemsControl.ItemPanel> 
     </ItemsControl> 

    </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

Ora, voglio che la mia posizione sia ridimensionabile, ma questo non rende bene a tutti dato che il layout di ogni singolo piccolo box è calcolata.
Questo potrebbe almeno per le dimensioni della casella essere eseguita una sola volta, poiché è uguale per tutte le caselle.

Qual è la procedura migliore per visualizzare un'enorme quantità di controlli in WPF/dove posso iniziare l'ottimizzazione? Le parole chiave mi aiuterebbero a continuare a scoprire le ottimizzazioni delle prestazioni per UniformGrids in WPF.

risposta

8

Ho riscontrato un problema simile durante l'utilizzo di un numero elevato di WPF DataGrid allo stesso tempo. E, infine, è sceso a due grandi temi:

  • I dizionari risorse
  • Attacchi

Resource Dizionari

Questi possono essere i principali colli di bottiglia se si usa di base WPF dispone senza essere attento.

Nel mio caso, per un unico rotolo logica nella ItemsControl che è stato che contiene il mio DataGrids, mi è stato sempre qualcosa come 10 milioni di chiamata a GetValue e GetValueWithoutLock su ResourceDictionary. Più di 1 secondo da elaborare.

Questo importante numero di ResourceDictionary accessi è stato causato da diverse fonti:

  • dinamica di recupero delle risorse: se si dispone ControlTemplates, e più in generale le risorse poste in alcuni dizionari di risorse, e li si accede tramite {DynamicResource ...}, rimuoverli . Avere qualcosa di statico da qualche parte e accedervi direttamente.
  • Recupero di stile: se non hai uno stile sul diverso Visual che usi, o se hai uno stile ma non hai impostato la proprietà FrameworkElement.OverridesDefaultStyle, WPF proverà a trovare uno stile che corrisponda al controllo in tutte le risorse, il che risulta in molti accessi ai dizionari delle risorse. Per evitare ciò, assicurarsi di ignorare tutti i modelli di controllo dei controlli e impostare Style={x:Null} su ogni singolo controllo (se è necessario uno stile per un controllo, impostarlo in linea sul proprio controllo e aggiungere OverridesDefaultStyle = true)
  • Implicito DataTemplates: i modelli di dati impliciti sono molto utili ma sono tutt'altro che gratuiti. Quando si cerca l'applicazione di DataTemplate, WPF sfoglia nuovamente i dizionari delle risorse per trovare uno DataTemplate corrispondente ai tipi ViewModels. La soluzione consiste nell'utilizzare un selettore DataTemplate per ogni controllo associato a un ViewModel e implementare un modo ottimizzato per recuperare il valore corretto DataTemplate. (Personalmente ho alcuni campi statici per il mio dataTemplate che recupero solo una volta da un resourceDictionary e uno DataTemplateSelector ottimizzato che li restituisce secondo necessità.)

Associazioni

Associazioni sono molto utili, ma molto costoso, la memoria e le prestazioni saggio saggio. In un ambiente molto sensato - dal punto di vista delle prestazioni - non puoi semplicemente usarli senza fare attenzione. Ad esempio, il binding può creare fino a 3 WeakReferences per ciascun oggetto binded, può usare reflection, ecc. Alla fine ho finito con la rimozione di quasi tutto il mio gestore di eventi binding e plugged sull'evento PropertyChanged di DataContext. Quando si modifica DataContext sui miei controlli (si dispone di un evento DataContextChanged), verifica se il mio DataContext supporta INotifyPropertyChanged e, in caso affermativo, allego un gestore di eventi nell'evento PropertyChanged e aggiorno le proprietà in base alle esigenze. (Ho avuto solo un modo di legare, quindi è per questo che ho scelto questo modo di fare le cose, ma ci sono altre soluzioni).

Può sembrare frustrante non utilizzare i binding quando si utilizzano MVVM e WPF, ma nel mio caso non c'era alcun modo per ottimizzare i binding.

Ultima cosa: se si dispone di oggetti Freezable (come i pennelli, ad esempio), non esitare a Freeze se possibile.

Questi sono alcuni consigli che possono aiutare a ottimizzare il codice. Avrai bisogno di un profiler (consiglio sempre di usare dotTrace dato che questo è il miglior profiler che abbia mai usato) per monitorare ciò che sta realmente accadendo e adattarlo in base ai risultati.

Buona fortuna!

+0

Grazie per la risposta dettagliata. Contiene già alcuni punti che posso migliorare nella mia applicazione (freezables, default style override). Controllerò questo e riferirò più tardi. –

+0

Certo, fammi sapere come va! In realtà è stato un buon esercizio per me perché devo scrivere un post sul mio blog :) – Sisyphe

+0

Abbiamo molti dizionari di risorse per tutto e l'applicazione è ancora molto veloce.Il modo in cui ci siamo avvicinati era creare un dizionario di risorse per una risorsa (come stile, vettore, pennello, ecc.), Quindi da ciascuna vista si fa riferimento solo a quei dizionari di risorse che sono necessari. Il problema è la griglia uniforme stessa, perché non virtualizza. – adminSoftDK

4

Se si è sicuri che tutte le scatole abbiano le stesse dimensioni, è sempre possibile creare la propria classe UniformGrid e sovrascrivere MeasureOverride per misurare un solo figlio. Potrei finire con qualche grande UniformGrid con controlli interni della stessa dimensione nel mio progetto attuale, quindi la tua idea mi ha intrigato e ho deciso di provarlo molto velocemente:

Ho usato la classe UniformGridAlmost di Petzold come fonte di ispirazione, ma la sottoclasse da UniformGrid anziché Panel pertanto non è necessario duplicare le proprietà di distribuzione delle righe e delle colonne. UniformGrid non sembra pubblicizzare il numero calcolato di righe e colonne per le sue classi derivate, quindi ho preso in prestito un'implementazione da Silverlight's UniformGrid class e ho utilizzato il metodo UpdateComputedValues.

Il MeasureOverride nella mia classe assomiglia a questo

protected override Size MeasureOverride(Size sizeAvailable) 
    { 
    if (InternalChildren.Count == 0) 
    { 
     return new Size(0, 0); 
    } 

    UpdateComputedValues(); 

    // Calculate a child size based on uniform rows and columns. 
    Size sizeChild = new Size(sizeAvailable.Width/ComputedColumns, 
           sizeAvailable.Height/ComputedRows); 

    // Assume children will measure to at least a comparable size. 
    UIElement child = InternalChildren[0]; 
    child.Measure(sizeChild); 

    double width = Math.Max(width, child.DesiredSize.Width); 
    double height = Math.Max(height, child.DesiredSize.Height); 
    return new Size(ComputedColumns * width, ComputedRows * height); 
    } 

Quindi, ecco il kicker, quando ho provato su un ItemsControl con 10000 TextBlocks, la mia versione di UniformGrid era più veloce, ma solo da una piccola quantità (meno dell'1% - Ho confermato che la chiamata a MeasureOverride è circa del 95% più veloce). Quindi probabilmente non è utile, ma ho pensato di condividere la mia esperienza. La tua osservazione che non è necessario misurare ogni scatola per determinare il layout uniforme nel tuo caso è vera, ma quella misurazione non è ciò che sta impantanando. Sono sicuro che è perché tutti quei controlli devono ancora essere disegnati, quindi verranno comunque misurati in seguito. Molto probabilmente otterrai molto di più dal suggerimento ResourceDictionary/Binding.

+0

Ho appena provato questo UniformGridAlmost così com'è, e nel mio caso è circa 3 volte più veloce della griglia uniforme standard. Quindi il tempo di disegno è sceso da 20 secondi a circa 7 secondi. – adminSoftDK

Problemi correlati