2011-01-25 7 views
17

Actualmente tengo un ListBox cuya colección ItemsSource está vinculada a una propiedad en mi viewmodel, de tipo IEnumerable. Cuando la referencia de esa preoprty cambia, el ListBox se actualiza como se esperaba, sin embargo, tengo un problema si tengo una gran colección de elementos y me desplazo al final del ListBox, y luego cambio la referencia a otra colección que contiene, por ejemplo, 1 elemento , la vista ListBox está en blanco y no se muestra la barra de desplazamiento. A continuación, debo desplazar el cuadro de lista hacia arriba con la rueda del mouse hasta que aparezca el 1 elemento.WPF - restablecer la posición de desplazamiento de ListBox cuando ItemsSource cambia

Entonces, lo que creo que busco es una forma de restablecer la posición de desplazamiento de ListBox a la parte superior, siempre que la propiedad ItemsSource cambie, para que siempre se muestre algo, independientemente de cuán grande o pequeña sea la colección.

+0

¿Entendí bien tu pregunta o estabas buscando algo más? –

+0

@Meleak ¡Gracias! ese enfoque será útil. De hecho, he rastreado el problema hasta un VirtualizingTilePanel que mi ListBox está utilizando y que he tomado de http://blogs.msdn.com/b/dancre/archive/2006/02/16/implementing-a-virtualizingpanel-part -4-the-goods.aspx. El problema se puede replicar en la descarga de muestra desplazándose hacia abajo, y luego reemplazando la colección enlazada con una que contenga un solo elemento. La vista queda en blanco hasta que te desplaces hacia arriba con la rueda del mouse. – devdigital

+0

Encontré una respuesta en http://social.msdn.microsoft.com/Forums/en/wpf/thread/05c5868b-7c91-4726-81fb-9af634c74d4a para VirtualizingTilePanel, pero he cambiado a la virtualización wrappanel desde binarymission - http://www.binarymission.co.uk/binaryvirtwrappanelsl3.htm – devdigital

Respuesta

20

Soy incapaz de reproducir su problema (para mí, la ListBox se desplaza hasta el último elemento de la nueva colección al cambiar ItemsSource). De todos modos, para desplazar el ListBox a la parte superior cada vez que cambia su ItemsSource puede usar algún código. Primero escucha a los cambios en la ItemsSourceProperty y luego desplazarse por la ListBox a la parte superior una vez que sus artículos se ha generado

actualización

Hecho un comportamiento adjunto que hace esto en su lugar para evitar código detrás. Se puede utilizar como esto

<ListBox ... 
     behaviors:ScrollToTopBehavior.ScrollToTop="True"/> 

ScrollToTopBehavior

public static class ScrollToTopBehavior 
{ 
    public static readonly DependencyProperty ScrollToTopProperty = 
     DependencyProperty.RegisterAttached 
     (
      "ScrollToTop", 
      typeof(bool), 
      typeof(ScrollToTopBehavior), 
      new UIPropertyMetadata(false, OnScrollToTopPropertyChanged) 
     ); 
    public static bool GetScrollToTop(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(ScrollToTopProperty); 
    } 
    public static void SetScrollToTop(DependencyObject obj, bool value) 
    { 
     obj.SetValue(ScrollToTopProperty, value); 
    } 
    private static void OnScrollToTopPropertyChanged(DependencyObject dpo, 
                DependencyPropertyChangedEventArgs e) 
    { 
     ItemsControl itemsControl = dpo as ItemsControl; 
     if (itemsControl != null) 
     { 
      DependencyPropertyDescriptor dependencyPropertyDescriptor = 
        DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl)); 
      if (dependencyPropertyDescriptor != null) 
      { 
       if ((bool)e.NewValue == true) 
       { 
        dependencyPropertyDescriptor.AddValueChanged(itemsControl, ItemsSourceChanged); 
       } 
       else 
       { 
        dependencyPropertyDescriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged); 
       } 
      } 
     } 
    } 
    static void ItemsSourceChanged(object sender, EventArgs e) 
    { 
     ItemsControl itemsControl = sender as ItemsControl; 
     EventHandler eventHandler = null; 
     eventHandler = new EventHandler(delegate 
     { 
      if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
      { 
       ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(itemsControl) as ScrollViewer; 
       scrollViewer.ScrollToTop(); 
       itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler; 
      } 
     }); 
     itemsControl.ItemContainerGenerator.StatusChanged += eventHandler; 
    } 
} 

Y una implementación de GetVisualChild

private 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

esto fue realmente útil :) –

1

Prueba esto:

if (listBox.Items.Count > 0) { 
    listBox.ScrollIntoView(listBox.Items[0]); 
} 
7

respuesta tardía:

Una solución sencilla es para agregar un controlador de eventos para el evento TargetUpdated, y establecer NotifyOnTargetUpdated=True en el ItemsSource vinculante:

<ListBox x:Name="listBox" 
     ItemsSource="{Binding MySource, NotifyOnTargetUpdated=True}" 
     TargetUpdated="ListBox_TargetUpdated"/> 

y en el controlador de eventos, desplácese al elemento superior:

private void ListBox_TargetUpdated(object sender, DataTransferEventArgs e) 
{ 
    if (listBox.Items.Count > 0) 
    { 
     listBox.ScrollIntoView(listBox.Items[0]); 
    } 
} 
0

Mejora de la respuesta de Fredrik Hedblad al trabajo con ObservableCollection:

public static class ItemsControlAttachedProperties 
{ 
    #region ScrollToTopOnItemsSourceChange Property 

    public static readonly DependencyProperty ScrollToTopOnItemsSourceChangeProperty = 
     DependencyProperty.RegisterAttached(
      "ScrollToTopOnItemsSourceChange", 
      typeof(bool), 
      typeof(ItemsControlAttachedProperties), 
      new UIPropertyMetadata(false, OnScrollToTopOnItemsSourceChangePropertyChanged)); 

    public static bool GetScrollToTopOnItemsSourceChange(DependencyObject obj) 
    { 
     return (bool) obj.GetValue(ScrollToTopOnItemsSourceChangeProperty); 
    } 

    public static void SetScrollToTopOnItemsSourceChange(DependencyObject obj, bool value) 
    { 
     obj.SetValue(ScrollToTopOnItemsSourceChangeProperty, value); 
    } 

    static void OnScrollToTopOnItemsSourceChangePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
    { 
     var itemsControl = obj as ItemsControl; 
     if (itemsControl == null) 
     { 
      throw new Exception("ScrollToTopOnItemsSourceChange Property must be attached to an ItemsControl based control."); 
     } 

     DependencyPropertyDescriptor descriptor = 
      DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl)); 
     if (descriptor != null) 
     { 
      if ((bool) e.NewValue) 
      { 
       descriptor.AddValueChanged(itemsControl, ItemsSourceChanged); 
      } 
      else 
      { 
       descriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged); 
      } 
     } 
    } 

    static void ItemsSourceChanged(object sender, EventArgs e) 
    { 
     var itemsControl = sender as ItemsControl; 
     DoScrollToTop(itemsControl); 

     var collection = itemsControl.ItemsSource as INotifyCollectionChanged; 
     if (collection != null) 
     { 
      collection.CollectionChanged += (o, args) => DoScrollToTop(itemsControl); 
     } 
    } 

    static void DoScrollToTop(ItemsControl itemsControl) 
    { 
     EventHandler eventHandler = null; 
     eventHandler = 
      delegate 
      { 
       if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
       { 
        var scrollViewer = GetVisualChild<ScrollViewer>(itemsControl); 
        scrollViewer.ScrollToTop(); 
        itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler; 
       } 
      }; 
     itemsControl.ItemContainerGenerator.StatusChanged += eventHandler; 
    } 

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

    #endregion 
} 
0

al formatear el control, se selecciona un rango de celdas como las opciones de selección que son t La niña aparece en el cuadro de lista. También selecciona una celda como enlace a las elecciones seleccionadas en las que se mostrará un número dependiendo de la posición de la selección en la lista. 1 para el primero en la lista, 2 para el segundo, etc.El código es bastante simple: -

Range ("A1") Seleccione

Selección = 1

Cambio ("A1") a la célula que haya vinculado y cambiar la 1 a la posición en la lista que quieres seleccionar

La referencia de celda es un enlace que funciona en ambos sentidos: si cambia su selección, el número en la celda cambia y si cambia el número en la celda, la selección resaltada cambia.

Cuestiones relacionadas