2011-08-31 7 views
5

Ho una casella di riepilogo che deve avere CanContentScroll == falso perché devo essere in grado di scorrere senza problemi. Ciò consente lo scorrimento fisico.Come scorrere fino alla pagina logica successiva in un ListBox con scorrimento fisico

Voglio anche scorrere la casella di riepilogo per pagina, ma se chiamo il metodo PageDown nella casella di riepilogo interna di ScrollViewer la prima riga viene tagliata perché l'altezza della casella di riepilogo non è un multiplo dell'altezza della riga.

Voglio che la prima riga sia sempre completamente visibile, come quando si utilizza lo scorrimento logico.

Qualcuno può darmi un suggerimento su come farlo?

risposta

2

Otterrete lo stesso effetto per una virtualizzazione non ItemsControl (ScrollViewer.CanContentScroll="False") come per una virtualizzazione se si scorre verso il basso e quindi si seleziona il contenitore superiore visibile con il mouse. Questo può anche essere fatto in codice.

Quando CanContentScroll è impostato su falso, la virtualizzazione è disattivata in modo che tutti i contenitori vengano generati in qualsiasi momento. Per ottenere il contenitore visibile in alto possiamo iterare i contenitori dall'alto fino a raggiungere lo VerticalOffset dello ScrollViewer. Una volta ottenuto, possiamo semplicemente chiamare BringIntoView su di esso e si allineerà in alto proprio come sarebbe se fosse utilizzata la virtualizzazione.

Esempio

<ListBox ItemsSource="{Binding MyCollection}" 
     ScrollViewer.CanContentScroll="False" 
     ScrollViewer.ScrollChanged="listBox_ScrollChanged" > 

chiamata BringIntoView sul contenitore superiore visibile nel gestore di eventi

private void listBox_ScrollChanged(object sender, ScrollChangedEventArgs e) 
{ 
    ItemsControl itemsControl = sender as ItemsControl; 
    ScrollViewer scrollViewer = e.OriginalSource as ScrollViewer; 
    FrameworkElement lastElement = null; 
    foreach (object obj in itemsControl.Items) 
    { 
     FrameworkElement element = itemsControl.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement; 
     double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset; 
     if (offset > e.VerticalOffset) 
     { 
      if (lastElement != null) 
       lastElement.BringIntoView(); 
      break; 
     } 
     lastElement = element; 
    } 
} 

Per solo ottenere questo effetto quando si desidera chiamare PageDown, in un clic del pulsante, ad esempio, è possibile creare un'estensione me thod for ListBox chiamato LogicalPageDown.

listBox.LogicalPageDown(); 

ListBoxExtensions

public static class ListBoxExtensions 
{ 
    public static void LogicalPageDown(this ListBox listBox) 
    { 
     ScrollViewer scrollViewer = VisualTreeHelpers.GetVisualChild<ScrollViewer>(listBox); 
     ScrollChangedEventHandler scrollChangedHandler = null; 
     scrollChangedHandler = (object sender2, ScrollChangedEventArgs e2) => 
     { 
      scrollViewer.ScrollChanged -= scrollChangedHandler; 
      FrameworkElement lastElement = null; 
      foreach (object obj in listBox.Items) 
      { 
       FrameworkElement element = listBox.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement; 
       double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset; 
       if (offset > scrollViewer.VerticalOffset) 
       { 
        if (lastElement != null) 
         lastElement.BringIntoView(); 
        break; 
       } 
       lastElement = element; 
      } 
     }; 
     scrollViewer.ScrollChanged += scrollChangedHandler; 
     scrollViewer.PageDown(); 
    } 
} 

ho notato nella sua domanda che già ottenuto il ScrollViewer ma io sono l'aggiunta di un'implementazione di GetVisualChild se qualcun altro si imbatte in questa domanda

public static T GetVisualChild<T>(DependencyObject parent) where T : Visual 
{ 
    T child = default(T); 

    int numVisuals = VisualTreeHelper.GetChildrenCount(parent); 
    for (int i = 0; i < numVisuals; i++) 
    { 
     Visual v = (Visual)VisualTreeHelper.GetChild(parent, i); 
     child = v as T; 
     if (child == null) 
     { 
      child = GetVisualChild<T>(v); 
     } 
     if (child != null) 
     { 
      break; 
     } 
    } 
    return child; 
} 
+0

@Zmaster: dimenticato che 'Margine' ecc. Può essere coinvolto quando si trova la posizione relativa del contenitore. Aggiornato la mia risposta di conseguenza –

+0

Alla fine ho deciso di utilizzare l'uso ScrollToOffset() calcolando l'offset di destinazione con l'ipotesi che ogni oggetto abbia la stessa altezza (questo è il mio caso). È più efficiente di iterare gli oggetti. La tua soluzione, d'altro canto, funziona anche sull'altezza di diversi oggetti, quindi l'ho accettata come risposta. – Zmaster

0

Se si scorre la parte superiore della "nuova pagina" in vista, otterrebbe quello che stai cercando? Vedi ScrollIntoView.

+0

Penso che l'oggetto apparirebbe in fondo alla lista al posto della parte superiore. Inoltre, non ho idea di come identificare quell'elemento. – Zmaster

Problemi correlati