2011-08-31 4 views
5

Tengo un cuadro de lista que debe tener CanContentScroll == falso porque necesito poder desplazarlo sin problemas. Esto permite el desplazamiento físico.Cómo desplazarse a la siguiente página lógica en un ListBox con desplazamiento físico

También quiero desplazar el listbox por página, pero si llamo al método PageDown en el visualizador interno ScrollViewer la primera fila se corta porque la altura de la listbox no es un múltiplo de la altura de la fila.

Quiero que la primera fila esté siempre completamente visible, como cuando se utiliza el desplazamiento lógico.

¿Alguien me puede dar una pista sobre cómo hacer eso?

Respuesta

2

Obtendrá el mismo efecto para una no virtualización ItemsControl (ScrollViewer.CanContentScroll="False") que para una virtualización si se desplaza hacia abajo y luego selecciona el contenedor superior visible con el mouse. Esto también se puede hacer en código.

Cuando CanContentScroll se configura como falso, la virtualización se desactiva para que todos los contenedores se generen en todo momento. Para obtener el contenedor visible superior, podemos iterar los contenedores desde la parte superior hasta que lleguemos al VerticalOffset del ScrollViewer. Una vez que lo tengamos simplemente podemos llamar al BringIntoView y se alineará muy bien en la parte superior como lo haría si se utilizara la virtualización.

Ejemplo

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

llamada BringIntoView en el recipiente superior visible en el controlador de eventos

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

Para única lograr este efecto cuando se quiere llamar PageDown, en un botón clic, por ejemplo, puedes crear una extensión thod para ListBox llamado 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(); 
    } 
} 

me di cuenta en su pregunta que usted ya tiene la ScrollViewer pero estoy añadiendo una implementación a GetVisualChild si alguien viene a través de esta pregunta

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: Olvidé que 'Margen', etc. puede estar involucrado al encontrar la posición relativa del contenedor. Actualicé mi respuesta según corresponda –

+0

Al final decidí utilizar ScrollToOffset() calculando el desplazamiento del objetivo con la suposición de que cada elemento tiene la misma altura (este es mi caso). Es más eficiente que repetir los elementos. Su solución, por otro lado, también funciona en diferentes alturas de elementos, así que lo acepté como la respuesta. – Zmaster

0

Si despliega el elemento superior de la "nueva página" para ver, ¿conseguiría eso lo que está buscando? Ver ScrollIntoView.

+0

Creo que el elemento aparecerá en la parte inferior del cuadro de lista en lugar de en la parte superior. Además, no tengo idea de cómo identificar ese artículo. – Zmaster

Cuestiones relacionadas