2012-12-14 13 views
12

Problem: Die Anzeige großer Datenmengen in einem scrollbaren Bereich hat eine schreckliche Leistung und/oder Benutzererfahrung.Problem mit der Virtualisierungsleistung bei großen scrollbaren Daten SL4

Versucht: Legen Sie ein DataTemplate in einer ListBox fest, um ein Raster mit aufgefüllten Daten anzuzeigen, wobei der VirtualizationMode auf Recycle und eine feste Höhe auf der ListBox festgelegt ist. So etwas wie das Beispiel unten.

<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> 

Die Content würde in einem Standard-<Grid> aus einer anderen Ansicht, die die allgemeine Anordnung der Elemente bevölkerten Formate bringen, bestehend aus etwa 20 statisch, und 20 Daten gebunden Textblöcken.

Dies funktioniert in Ordnung, und die anfänglichen Lasten halbieren. JETZT, jetzt ist das Problem, ich brauche die Fähigkeit für die Höhe, um nicht eine feste Größe zu sein, so nimmt es den Raum, der in seinem Elternteil zur Verfügung steht und kann sogar die Größe geändert werden. Dank @DanFox habe ich herausgefunden, dass Sie die Höhe in der einen oder anderen Form korrigieren müssen, um die Virtualisierung aufzurufen, oder die RenderEngine denkt einfach, dass sie sowieso unendlichen Raum hat.

Die Frage ist: Gibt es eine bessere Möglichkeit, dies zu tun, oder wie kann ich zumindest die aktuelle Technik beheben, um bessere UX zu ermöglichen? Ich erzeuge möglicherweise Hunderte von diesen Elementen, also brauche ich die Leistungssteigerung der Virtualisierung. Allerdings muss ich dem Benutzer auch erlauben, die Größe des Fensters zu ändern und die Fähigkeit zum Scrollen effektiv beizubehalten.

Jeder Einblick wird sehr geschätzt, danke und frohe Feiertage!

+0

Haben Sie versucht, die Höhe des ScrollViewers und anderer Komponenten vorübergehend zu korrigieren? Manchmal kann der Leistungsabfall dadurch verursacht werden, dass die Layout-Engine dem ScrollViewer eine unendliche Höhe verleiht. Entschuldigung, ich kann nicht expliziter sein, ich bin in diesem Bereich ein wenig eingerostet, habe seit einiger Zeit keinen SL mehr gemacht ... –

+0

Werden deine Daten schnell geladen? An welchen Sammlertyp sind Sie gebunden? –

+0

@Dan Fox Also nur hartes Fixieren der Höhe des Scrollviewers könnte die Rendering-Geschwindigkeit beeinflussen? –

Antwort

1

Ok also hier ist was ich getan habe und muss sagen, es ist eine große Verbesserung! Wir begannen mit einer Ladezeit von 77 Sekunden auf den Punkt für dieses spezielle Stück. Mit etwas Refactoring, wie wir uns auf der Rückseite verbanden, rasierten wir ungefähr 20 Sekunden ab, so dass das Problem immer noch im UI-Rendering lag. Die Antwort unten ist offensichtlich einfacher als ich ursprünglich dachte, und jetzt laden wir riesige Datenmengen in 15-20 Sekunden (mit Virtualisierung natürlich) und kleinere Lasten sind im Grunde augenblicklich, die uns wieder auf den richtigen Weg bringen.

Hier ist was ich getan habe;

<!-- 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> 

Und das war's! Voila! Alles was es brauchte, war das ControlTemplate zu lösen und einen direkten ItemsPresenter anzuwenden! Es virtualisiert sich jetzt großartig, es passt sich an, das Gleichgewicht ist wiederhergestellt und ich will kein Einhorn mehr schlagen. Danach habe ich nur die visuellen Zustände entfernt, weil wir keine Art von Objektauswahl brauchten, und das ist es auch, danke für jeden Einblick! Entschuldigung, ich konnte dieses Mal kein Kopfgeld vergeben.

1

wie gewünscht :-) Ich hatte nicht das Gefühl, wie ich „antwortete“ hatte alles so weit ...

Sie haben völlig Recht, sollte es eine Möglichkeit, die Höhe Dynamik bekommen. Wenn Sie es auf feste Höhe einstellen, sollten Sie schnell prüfen, ob die Virtualisierung funktioniert. Haben Sie mit dem Performance-Profiler etwas erreicht oder vielleicht SilverlightSpy? Ich habe eines dieser Probleme beseitigt, indem ich die Bindung auf der UI/VM umgestaltet habe, um sie effizienter zu machen. Es gab eine gute Ressource für WinPhone7 SL mit einer potenziell guten Lösung, ich werde sehen, ob ich es ausgraben kann (die Theorie sollte auf Vollfett-SL übertragen werden)

This hat einen guten Punkt auf Caching abhängig von Ihre VM-Architektur

This könnte nützlich sein, da es Laxy Laden ein wenig mehr

Und ein couple of hints vom WinPho 7 Entwicklerteam erklärt.

Hoffnung, die

0

Wenn seine nur aufgrund der festgelegten Höhe hilft, warum nicht Sie dies nur versuchen:

<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> 

wichtig, dass Sie die List-Box in einen Behälter, der in den Weltraum passt und nicht zu Innenraum wie ein Stackpanel.

Versuchen Sie dies und sagen Sie mir, wenn es hilft. :)

+0

Ich könnte mich irren, aber ich denke nicht, dass Sie RelativeSource so verwenden können SL4? Ich denke, es wurde in SL5 verbessert? –

+0

Dan ist korrekt, SL4 macht nur Self und TemplatedParent, AncestorType ist ein Nein, das ist eine Schande, weil ich in Instanzen stoße, wo ich es die ganze Zeit verwenden könnte. Auf derselben Route versuchte ich jedoch Height = {Binding ActualHeight, ElementName = ParentGridContainter} und hatte auch kein Glück. Ich habe einen Textblock an die Eigenschaft gebunden und es gibt eine völlig ungültige Zahl zurück ... aber das ist ein anderes Thema lol. –

+0

Ja Im sorry AncestorType ist neu in SL5. Versuchen Sie, an Höhe und nicht ActualHeight zu binden –

0

Ich denke, wenn Sie magische UI-Steuerelemente in Ihr Datatemplate ziehen, besiegen Sie den Zweck der Virtualisierung. Werden diese Kontrollen (Grids) selbst wiederverwendet? Was passiert, wenn sich die {Binding} ändert?Wie viel Code wird in einem Verhalten ausgeführt, das wie ein angehängtes Verhalten aussieht? Vielleicht konnte das, was Sie getan haben, performant gemacht werden. Aber wenn Sie einfach ein einfaches Datatemplate erstellen (oder ein normales UserControl einfügen), werden Sie sicher sein, dass alle darin enthaltenen Steuerelemente wiederverwendet werden, und es wird eine minimale Strafe beim Wechseln ihres Datenkontextes geben.

Ich bin mir nicht sicher über das Problem mit Höhe. Es sollte funktionieren (eine begrenzte Höhe in Arrange erzielen), aber es hängt davon ab, was es unter der Haube tut (vielleicht versucht es alles herauszufinden, bevor Arrange die Höhe zur Verfügung stellt).

Sie können immer Ihr eigenes virtualisierendes itemscontrol erstellen. Von Panel ableiten, in einen ScrollViewer einfügen und IScrollInfo im Panel implementieren. Dann werden Sie alle Gründe wissen, warum die Dinge so sind wie sie sind :)

+0

Sie können auch versuchen, mit VirtualizingStackPanel direkt in einem ScrollViewer. Dies könnte eine bessere Möglichkeit sein, die Virtualisierung zu erzwingen. Sie können auch von VirtualizationStackPanel ableiten, was die Vorteile von Panel ableitet, aber mit weniger Aufwand. –

0

Ich bin mir nicht sicher, ob das eine akzeptable Lösung für Ihren Anwendungsfall wäre, aber ich habe ein ContentControl mit dem Namen DelayedLayoutUpdateContainer erstellt ein komplexes Layout und hilft, die Leistung zu verbessern. Die Idee hinter dem DelayedLayoutUpdateContainer ist, dass es die Größe seines Inhalts nur dann anpasst, wenn es für eine bestimmte Zeitspanne nicht selbst angepasst wurde. Die Größe des ContentPresenters in der ControlTemplate wird auf absolute Werte festgelegt. Dies kann den gleichen Effekt haben wie die Höhe der ListBox auf einen absoluten Wert zu setzen.