2012-08-25 19 views
7

Ho un ItemsControl che visualizza i suoi articoli in un ScrollViewer e esegue la virtualizzazione. Sto provando a scorrere quello ScrollViewer in un oggetto (fuori dallo schermo, quindi virtualizzato) che contiene. Tuttavia, poiché l'elemento è virtualizzato, non esiste realmente sullo schermo e non ha posizione (IIUC).Scorrere fino a un elemento di virtualizzazione ItemsControl

Ho provato BringIntoView sull'elemento figlio, ma non scorre in vista. Ho anche provato a farlo manualmente con TransformToAncestor, TransformBounds e ScrollToVerticalOffset, ma TransformToAncestor non ritorna mai (credo anche a causa della virtualizzazione, perché non ha posizione, ma non ne ho la prova) e il codice dopo non viene mai eseguito.

È possibile scorrere fino a un elemento con virtualizzazione ItemsControl? Se é cosi, come?

risposta

9

Girare nel codice sorgente .NET mi porta a consigliarvi l'uso di un ListBox e il suo metodo ScrollIntoView. L'implementazione di questo metodo si basa su alcuni metodi internal come VirtualizingPanel.BringIndexIntoView che impone la creazione dell'elemento a quell'indice e scorre ad esso. Il fatto che molti di questi meccanismi siano interni significa che se provi a farlo da solo hai un brutto momento.

(Per fare la selezione questo porta con sé invisibile si può retemplate la ListBoxItems)

+0

Vorrei evitare di farlo perché non ho bisogno della funzionalità "seleziona un elemento" di "ListBox".Qualche idea sul perché 'ItemsControl' non ha' ScrollIntoView'? –

+0

@Seth: Come ho detto, puoi nascondere la selezione, a chi importa se è lì? Non ha lo scrolling perché è stato progettato in questo modo, 'ItemsControl' è il più basilare dei controlli degli elementi, la funzionalità di scorrimento non è necessaria per una classe di base. –

+0

Ora per scoprire come fare in modo che 'ListBox' lasci scorrere completamente un oggetto in vista quando si fa clic ... –

11

Ho cercato di ottenere un ItemsControl con un VirtualizingStackPanel per spostarsi su un elemento per un po 'di tempo, e continuai a trovare il "usa un ListBox" risposta. Non volevo, quindi ho trovato un modo per farlo. Per prima cosa devi configurare un modello di controllo per il tuo ItemsControl che contiene un ScrollViewer (che probabilmente hai già se usi un controllo per gli oggetti). Il mio modello di base sia simile alla seguente (contenuto in uno stile a portata di mano per l'ItemsControl)

<Style x:Key="TheItemsControlStyle" TargetType="{x:Type ItemsControl}"> 
    <Setter Property="Template"> 
    <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ItemsControl}"> 
       <Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True"> 
        <ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False" HorizontalScrollBarVisibility="Auto"> 
         <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" /> 
        </ScrollViewer> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

così ho fondamentalmente ha un confine con un visualizzatore di scorrimento thats di andare a contenere il mio contenuto.
mio ItemsControl è definita con:

<ItemsControl x:Name="myItemsControl" [..snip..] Style="{DynamicResource TheItemsControlStyle}" ScrollViewer.CanContentScroll="True" VirtualizingStackPanel.IsVirtualizing="True"> 

Ok ora la parte divertente. Ho creato un metodo di estensione per collegare a qualsiasi ItemsControl per farlo scorrere fino alla voce data:

public static void VirtualizedScrollIntoView(this ItemsControl control, object item) { 
     try { 
      // this is basically getting a reference to the ScrollViewer defined in the ItemsControl's style (identified above). 
      // you *could* enumerate over the ItemsControl's children until you hit a scroll viewer, but this is quick and 
      // dirty! 
      // First 0 in the GetChild returns the Border from the ControlTemplate, and the second 0 gets the ScrollViewer from 
      // the Border. 
      ScrollViewer sv = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild((DependencyObject)control, 0), 0) as ScrollViewer; 
      // now get the index of the item your passing in 
      int index = control.Items.IndexOf(item); 
      if(index != -1) { 
       // since the scroll viewer is using content scrolling not pixel based scrolling we just tell it to scroll to the index of the item 
       // and viola! we scroll there! 
       sv.ScrollToVerticalOffset(index); 
      } 
     } catch(Exception ex) { 
      Debug.WriteLine("What the..." + ex.Message); 
     } 
    } 

Quindi, con il metodo di estensione in atto lo si dovrebbe utilizzare come metodo di compagno di ListBox:

myItemsControl.VirtualizedScrollIntoView(someItemInTheList); 

Funziona alla grande!

Nota che puoi anche chiamare sv.ScrollToEnd() e gli altri metodi di scorrimento usuali per aggirare i tuoi articoli.

+0

Sfortunatamente io uso lo scorrimento basato su pixel, quindi questo non funziona per me, ma sono sicuro che questo aiuterà gli altri in futuro, +1. –

+0

Se si utilizza lo scrolling basato su pixel, è possibile ottenere le dimensioni di un singolo oggetto in ItemsControl (se è di dimensioni fisse è facile, ma ci sono anche enumerazioni che è possibile fare su ItemControls ItemTemplate per ottenere le dimensioni di un individuo item) e quindi basta moltiplicare l'indice restituito dalla dimensione di un singolo oggetto e quindi chiamare ScrollToVerticalOffset con quel numero. per esempio sv.ScrollToVerticalOffset ((double) index * sizeOfAnItemInTheList); –

0

So di essere abbastanza in ritardo alla festa, ma spero che questo possa aiutare qualcun altro venendo cercando la soluzione ...

int index = myItemsControl.Items.IndexOf(*your item*).FirstOrDefault(); 
int rowHeight = *height of your rows*; 
myScrollView.ScrollToVerticalOffset(index*rowHeight); 
//this will bring the given item to the top of the scrollViewer window 

... e il mio XAML è configurato in questo modo ...

<ScrollViewer x:Name="myScrollView"> 
    <ItemsControl x:Name="myItemsControl"> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <Grid> 
        <!-- data here --> 
       </Grid> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</ScrollViewer>