Полоса прокрутки списка не соответствует выбранному элементу (с ICollectionView)

2

Я пытаюсь реализовать шаблон MVVM в WPF. Я следил за Джереми Аллесом Очень простое демонстрационное приложение MVVM. У меня есть ListBox, который имеет привязку к ObservableCollection:

<ListBox
    Name="myListBox"
    IsSynchronizedWithCurrentItem="True"
    ItemsSource="{Binding Persons}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <views:PersonsView />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Я добавил ICollectionView для управления выбранным элементом в ListBox. Это также позволяет мне иметь две кнопки, которые позволяют мне выбирать предыдущие и следующие элементы в списке.

private void GoToPrevious()
{
    this.collectionView.MoveCurrentToPrevious();
}
private void GoToNext()
{
    this.collectionView.MoveCurrentToNext();
}

Все работает отлично, однако, когда выбранный элемент находится ниже отображаемой области списка, панель прокрутки списка не перемещается соответственно.

Как синхронизировать полосу прокрутки/область отображения ListBox с выбранным элементом?

Теги:
wpf
observablecollection
mvvm
listbox

1 ответ

6
Лучший ответ

Я нашел ответ. Мне нужно было использовать

myListBoxItem.BringIntoView();

Проблема заключалась в том, что я не хотел добавлять код, поскольку я реализую MVVM.

В решении используется Приложенное поведение. Джош Смит имеет отличную статью об этом: Введение в приложенные действия в WPF.

Я применил Setter к стилю элементов в ListBox:

<ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
        <Setter
            Property="custom:ListBoxItemBehavior.IsBroughtIntoViewWhenSelected"
            Value="True" />
    </Style>
</ListBox.ItemContainerStyle>

И добавил следующий класс (только изменил TreeView из статьи Джоша в ListBox):

public static class ListBoxItemBehavior
{
    #region IsBroughtIntoViewWhenSelected

    public static bool GetIsBroughtIntoViewWhenSelected(ListBoxItem listBoxItem)
    {
        return (bool)listBoxItem.GetValue(IsBroughtIntoViewWhenSelectedProperty);
    }

    public static void SetIsBroughtIntoViewWhenSelected(
      ListBoxItem listBoxItem, bool value)
    {
        listBoxItem.SetValue(IsBroughtIntoViewWhenSelectedProperty, value);
    }

    public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty =
        DependencyProperty.RegisterAttached(
        "IsBroughtIntoViewWhenSelected",
        typeof(bool),
        typeof(ListBoxItemBehavior),
        new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged));

    static void OnIsBroughtIntoViewWhenSelectedChanged(
      DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        ListBoxItem item = depObj as ListBoxItem;
        if (item == null)
            return;

        if (e.NewValue is bool == false)
            return;

        if ((bool)e.NewValue)
            item.Selected += OnListBoxItemSelected;
        else
            item.Selected -= OnListBoxItemSelected;
    }

    static void OnListBoxItemSelected(object sender, RoutedEventArgs e)
    {
        // Only react to the Selected event raised by the ListBoxItem
        // whose IsSelected property was modified.  Ignore all ancestors
        // who are merely reporting that a descendant Selected fired.
        if (!Object.ReferenceEquals(sender, e.OriginalSource))
            return;

        ListBoxItem item = e.OriginalSource as ListBoxItem;
        if (item != null)
            item.BringIntoView();
    }

    #endregion // IsBroughtIntoViewWhenSelected
}

Это работает!!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню