wpf listbox scroll itemssource

listboxitem wpf



WPF: restablece la posiciĆ³n de desplazamiento de ListBox cuando cambia ItemsSource (5)

Cuando formatea el control, selecciona un rango de celdas como las opciones de selección que luego se enumeran en el cuadro de lista. También selecciona una celda como enlace a las opciones 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:

Rango ("A1") Seleccione

Selección = 1

Cambie ("A1") a la celda que ha vinculado y cambie el 1 a la posición en la lista que desea seleccionar.

La referencia de celda que es un enlace 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.

Actualmente tengo un ListBox cuya colección ItemsSource está vinculada a una propiedad en mi viewmodel, de tipo IEnumerable. Cuando cambia la referencia de esa propiedad, el ListBox se actualiza como se esperaba, sin embargo, tengo un problema porque si tengo una gran colección de elementos y me desplazo a la parte inferior del ListBox, y luego cambio la referencia a otra colección que contenga, digamos, 1 elemento , la vista del cuadro de lista está en blanco y no se muestra ninguna barra de desplazamiento. Tengo que desplazar el cuadro de lista hacia arriba con la rueda del ratón hasta que aparezca el elemento 1.

Entonces, lo que creo que estoy buscando es una forma de restablecer la posición de desplazamiento del ListBox a la parte superior, siempre que la propiedad ItemsSource cambie, de modo que siempre se muestre algo, sin importar cuán grande o pequeña sea la colección.


Prueba esto:

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


Respuesta mejorada de Fredrik Hedblad para trabajar 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 }


Respuesta tardía :

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

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

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

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


No puedo reproducir su problema (para mí, el 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 se ItemsSource , puede usar algún código detrás. Primero escuche los cambios en ItemsSourceProperty y luego desplace el ListBox a la parte superior una vez que se hayan generado sus elementos

Actualizar

Hizo un comportamiento adjunto que hace esto en lugar de evitar el código detrás. Se puede utilizar así.

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