2009-12-30 18 views
25

Il mio prototipo visualizza "documenti" che contengono "pagine" che sono rappresentate da immagini in miniatura. Ogni documento può avere un numero qualsiasi di pagine. Ad esempio, potrebbero essere disponibili 1000 documenti di 5 pagine ciascuno o 5 documenti con 1000 pagine ciascuno o in qualche punto all'interno. I documenti non contengono altri documenti. Nel mio markup xaml ho un ListBox, il cui ItemsTemplate fa riferimento a innerItemsTemplate che ha anche un ListBox. Voglio i livelli 2 di elementi selezionati in modo che io possa eseguire varie operazioni su documenti o pagine (eliminare, unire, spostare in una nuova posizione, ecc.). The innerItemsTemplate ListBox utilizza uno WrapPanel come ItemsPanelTemplate.ListBox WPF con ListBox - UI Virtualization and Scrolling

Per lo scenario in cui ho un gran numero di documenti con un paio di pagine ciascuno (diciamo, 10.000 documenti con 5 pagine ciascuno), lo scorrimento grandi opere grazie all'interfaccia utente di virtualizzazione dal VirtualizingStackPanel. Tuttavia, ho problemi se ho un numero elevato di pagine. Un documento con 1000 pagine visualizzerà solo circa 50 alla volta (qualsiasi cosa si adatti allo schermo) e quando si scorre verso il basso, l'esterno ListBox si sposta sul documento successivo, saltando le 950 pagine che non erano visibili. Insieme a questo, non c'è VirtualzingWrapPanel quindi la memoria dell'app aumenta davvero.

Mi chiedo se sto andando su questo nel modo giusto, in particolare poiché è una specie di difficile da spiegare! Vorrei poter visualizzare 10000 documenti con 1000 pagine ciascuno (mostrando solo ciò che si adatta allo schermo), utilizzando la virtualizzazione dell'interfaccia utente e lo scorrimento uniforme.

Come posso essere sicuro che lo scorrimento si sposti attraverso tutte le pagine del documento prima di visualizzare il documento successivo e mantenere comunque la virtualizzazione dell'interfaccia utente? La barra di scorrimento sembra spostarsi solo sul prossimo documento.

Sembra logico rappresentare "documenti" e "pagine" - con il mio attuale metodo di utilizzo di ListBox all'interno di un ListBox?

Apprezzerei molto qualsiasi idea tu abbia. Grazie.

risposta

24

La risposta qui è sorprendente:

  • Se si utilizza ItemsControl o ListBox si otterrà il comportamento si verifica, in cui il controllo scorre "per oggetto" in modo da saltare su un intero documento in una volta, MA
  • Se si utilizza TreeView, il controllo scorrerà senza interruzioni in modo da poter scorrere il documento e nel successivo, ma sarà comunque capace di virtualizzare.

Penso che il motivo il team ha scelto WPF questo comportamento è che TreeView ha comunemente elementi che sono più grande dell'area visibile, mentre in genere ListBox es no.

In ogni caso, è banale in WPF per fare uno sguardo TreeView e agire come un ListBox o ItemsControl semplicemente modificando il ItemContainerStyle. Questo è molto semplice. Puoi eseguire il rollover o copiare semplicemente il modello appropriato dal file del tema di sistema.

in modo da avere qualcosa di simile:

<TreeView ItemsSource="{Binding documents}"> 
    <TreeView.ItemsPanel> 
    <ItemsPanelTemplate> 
     <VirtualizingStackPanel /> 
    </ItemsPanelTemplate> 
    </TreeView.ItemsPanel> 
    <TreeView.ItemContainerStyle> 
    <Style TargetType="{x:Type TreeViewItem}"> 
     <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type TreeViewItem}"> 
      <ContentPresenter /> <!-- put your desired container style here with a ContentPresenter inside --> 
      </ControlTemplate> 
     </Setter.Value> 
     </Setter> 
    </Style> 
    </TreeView.ItemContainerStyle> 
    <TreeView.ItemTemplate> 
    <DataTemplate TargetType="{x:Type my:Document}"> 
     <Border BorderThickness="2"> <!-- your document frame will be more complicated than this --> 
     <ItemsControl ItemsSource="{Binding pages}"> 
      ... 
     </ItemsControl> 
     </Border> 
    </DataTemplate> 
    </TreeView.ItemTemplate> 
</TreeView> 

Getting scorrimento pixel-based e multiselect ListBox stile di lavorare insieme

Se si utilizza questa tecnica per ottenere lo scorrimento pixel-based, il tuo ItemsControl esterno che mostra i documenti non può essere un ListBox (perché ListBox non è una sottoclasse di TreeView o TreeViewItem). Così perdi tutto il supporto multiselect di ListBox. Per quanto posso dire, non c'è modo di utilizzare queste due caratteristiche insieme senza includere parte del proprio codice per una funzione o l'altra.

Se avete bisogno di entrambe le serie di funzionalità nello stesso controllo, si hanno sostanzialmente diverse opzioni:

  1. Implementare selezione multipla se stessi in una sottoclasse di TreeViewItem. Utilizzare TreeViewItem anziché TreeView per il controllo esterno, poiché consente di selezionare più bambini. Nel modello all'interno di ItemsContainerStyle: aggiungi un CheckBox attorno a ContentPresenter, il modello associa il CheckBox a IsSelected e modella il CheckBox con il modello di controllo per ottenere l'aspetto desiderato. Quindi aggiungi i tuoi gestori di eventi mouse per gestire Ctrl-Click e Maiusc-Click per multiselect.

  2. Implementare la virtualizzazione con scorrimento a pixel in una sottoclasse di VirtualizingPanel. Questo è relativamente semplice, poiché la maggior parte della complessità di VirtualizingStackPanel è correlata allo scrolling non-pixel e al riciclo dei contenitori. Dan Crevier's Blog ha alcune informazioni utili per comprendere VirtualizingPanel.

+0

Questo approccio funziona davvero per me per quanto riguarda la virtualizzazione dell'interfaccia utente. Ora ho solo bisogno di ottenere il comportamento di ListBox per la selezione degli elementi (pagine o documenti in questo caso). Come posso ottenere modalità di selezione multiple ed estese simili a ListBox? –

+0

Inoltre, sto impostando ItemsPanelTemplate all'interno di ItemsControl su un WrapPanel, che non sembra avvolgere quando ridimensiono l'app - sembra comportarsi più come uno stackPanel. Nel complesso, sento che la risposta di cui sopra Ray mi fa andare nella giusta direzione. –

+0

Sono così felice di essere incappato in questo post che mi ha salvato strappandomi tutti i capelli. – Bijington

0

Si prega di consentire a questa prefazione di rispondere a una domanda: l'utente deve vedere ogni singola miniatura all'interno di ogni elemento nell'elenco in ogni momento?

Se la risposta a questa domanda è 'no', allora forse sarebbe possibile limitare il numero di pagine visibili all'interno del modello di oggetto interno (dato che hai indicato che lo scorrimento funziona bene con, diciamo, 5 pagine) e utilizzare un modello separato 'elemento selezionato' che è più grande e visualizza tutte le pagine per quel documento?Billy Hollis spiega come 'pop' un elemento selezionato in una casella di riepilogo su dnrtv episode 115

+0

senza che l'utente non ha bisogno di vedere ogni miniatura all'interno di ogni elemento della lista in ogni momento - hanno solo bisogno di essere in grado di scorrere per arrivare ad altri articoli –

36

E 'possibile ottenere VirtualizingStackPanels scorrimento uniforme in WPF 4.0 senza sacrificare la virtualizzazione, se siete pronti a utilizzare la reflection per accedere alle funzionalità privata del VirtualizingStackPanel.Tutto ciò che devi fare è impostare la proprietà privata IsPixelBased di VirtualizingStackPanel su true.

Nota che in .Net 4.5 non è necessario questo hack in quanto è possibile impostare VirtualizingPanel.ScrollUnit = "Pixel".

per renderlo veramente facile, ecco qualche codice:

public static class PixelBasedScrollingBehavior 
{ 
    public static bool GetIsEnabled(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(IsEnabledProperty); 
    } 

    public static void SetIsEnabled(DependencyObject obj, bool value) 
    { 
     obj.SetValue(IsEnabledProperty, value); 
    } 

    public static readonly DependencyProperty IsEnabledProperty = 
     DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(PixelBasedScrollingBehavior), new UIPropertyMetadata(false, HandleIsEnabledChanged)); 

    private static void HandleIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var vsp = d as VirtualizingStackPanel; 
     if (vsp == null) 
     { 
      return; 
     } 

     var property = typeof(VirtualizingStackPanel).GetProperty("IsPixelBased", 
                    BindingFlags.NonPublic | BindingFlags.Instance); 

     if (property == null) 
     { 
      throw new InvalidOperationException("Pixel-based scrolling behaviour hack no longer works!"); 
     } 

     if ((bool)e.NewValue == true) 
     { 
      property.SetValue(vsp, true, new object[0]); 
     } 
     else 
     { 
      property.SetValue(vsp, false, new object[0]); 
     } 
    } 
} 

Per utilizzare questo su un ListBox, per esempio, si potrebbe fare:

<ListBox> 
    <ListBox.ItemsPanel> 
     <ItemsPanelTemplate> 
     <VirtualizingStackPanel PixelBasedScrollingBehavior.IsEnabled="True"> 
      </VirtualizingStackPanel> 
     </ItemsPanelTemplate> 
    </ListBox.ItemsPanel> 
</ListBox> 
+19

Questo è stato davvero utile, +1. Per i futuri visitatori che utilizzano .NET 4.5, è necessario impostare 'VirtualizingPanel.ScrollUnit =" Pixel "' sul tuo 'ListBox' stesso, non sul' VirtualizingStackPanel' che contiene il contenuto. –

+0

Ok, questo non funziona per me. Non so perché. –

+0

@ViktorLaCroix: hai installato .Net 4.5 sulla tua macchina? Perché questo è un aggiornamento sul posto a .Net 4.0, penso che rompa questo hack. –

4

Questo ha funzionato per me. Sembra un paio di attributi semplici lo farà (NET 4,5)

<ListBox    
    ItemsSource="{Binding MyItems}" 
    VirtualizingStackPanel.IsVirtualizing="True" 
    VirtualizingStackPanel.ScrollUnit="Pixel"/>