WPF - reset Позиция прокрутки ListBox при изменении ItemsSource
В настоящее время у меня есть ListBox, чья коллекция ItemsSource привязана к свойству на моей модели просмотра типа IEnumerable. Когда эта ссылка preoprty изменяется, ListBox обновляется, как и ожидалось, однако у меня есть проблема в том, что если у меня есть большой набор элементов и прокручивается до нижней части ListBox, а затем измените ссылку на другую коллекцию, содержащую, скажем, 1 элемент, вид ListBox пуст и не отображается полоса прокрутки. Затем я должен прокрутить список с помощью колеса мыши до тех пор, пока не появится 1 пункт.
Итак, я думаю, что мне нужно, это способ сброса позиции прокрутки ListBox вверху, всякий раз, когда свойство ItemsSource изменяется, так что что-то всегда отображается независимо от того, насколько большая или малая коллекция.
Ответы
Ответ 1
Я не могу воспроизвести вашу проблему (для меня ListBox
прокручивается до последнего элемента в новой коллекции при изменении ItemsSource
). В любом случае, чтобы прокручивать ListBox
до вершины каждый раз при изменении ItemsSource
, вы можете использовать какой-то код позади. Сначала прослушайте изменения в ItemsSourceProperty
, а затем прокрутите ListBox
вверху после того, как его элементы были сгенерированы.
Обновить
Сделал приложенное поведение, которое делает это вместо этого, чтобы избежать кода. Его можно использовать следующим образом
<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;
}
}
И реализация 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;
}
Ответ 2
Поздний ответ:
Простым решением является добавление обработчика событий для события TargetUpdated
и установка NotifyOnTargetUpdated=True
в привязке ItemsSource
:
<ListBox x:Name="listBox"
ItemsSource="{Binding MySource, NotifyOnTargetUpdated=True}"
TargetUpdated="ListBox_TargetUpdated"/>
и в обработчике событий выделите верхний элемент:
private void ListBox_TargetUpdated(object sender, DataTransferEventArgs e)
{
if (listBox.Items.Count > 0)
{
listBox.ScrollIntoView(listBox.Items[0]);
}
}
Ответ 3
Попробуйте следующее:
if (listBox.Items.Count > 0) {
listBox.ScrollIntoView(listBox.Items[0]);
}
Ответ 4
Усовершенствован Fredrik Hedblad для работы с 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
}
Ответ 5
При форматировании элемента управления вы выбираете диапазон ячеек в качестве вариантов выбора, которые затем перечисляются в списке. Вы также выбираете ячейку в качестве ссылки на выбранные варианты, в которых число будет отображаться в зависимости от позиции выбора в списке. 1 для первого в списке, 2 для второго и т.д. Код довольно просто: -
Range ( "A1" ) Выберите
Выбор = 1
Измените ( "A1" ) на ячейку, которую вы связали
и измените 1 на позицию в списке, который вы хотите выбрать.
Ссылка на ячейку, являющаяся ссылкой, работает в обоих направлениях - если вы измените свой выбор, число в ячейке изменится, и если вы измените число в ячейке, выбранный выбор изменится.