Подтвердить что ты не робот

ScrollIntoView и ListView с виртуализацией

У меня есть ListView (по умолчанию включена виртуализация), которая ItemsSource привязана к свойству ObservableCollection<Item>.

Когда данные заполняются (свойство установлено, и уведомление увеличивается) Я вижу 2 макета раскладки в профилировщике, второй - после вызова listView.ScrollIntoView().

Мое понимание:

  • ListView загружает данные через привязку и создает ListViewItem для элементов на экране, начиная с индекса 0.
  • Затем я вызываю listView.ScrollIntoView().
  • И теперь ListView делает это второй раз (создание ListViewItem s).

Как предотвратить эту деин виртуализацию в два раза (я не хочу, чтобы это произошло до ScrollIntoView)?


Я попытался сделать репрограммирование с помощью ListBox.

XAML:

<Grid>
    <ListBox x:Name="listBox" ItemsSource="{Binding Items}">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="IsSelected" Value="{Binding IsSelected}" />
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
    <Button Content="Fill" VerticalAlignment="Top" HorizontalAlignment="Center" Click="Button_Click" />
</Grid>

CS:

public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}

public class ViewModel : NotifyPropertyChanged
{
    public class Item : NotifyPropertyChanged
    {
        bool _isSelected;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                _isSelected = value;
                OnPropertyChanged();
            }
        }
    }

    ObservableCollection<Item> _items = new ObservableCollection<Item>();
    public ObservableCollection<Item> Items
    {
        get { return _items; }
        set
        {
            _items = value;
            OnPropertyChanged();
        }
    }
}

public partial class MainWindow : Window
{
    ViewModel _vm = new ViewModel();

    public MainWindow()
    {
        InitializeComponent();
        DataContext = _vm;
    }

    void Button_Click(object sender, RoutedEventArgs e)
    {
        var list = new List<ViewModel.Item>(1234567);
        for (int i = 0; i < 1234567; i++)
            list.Add(new ViewModel.Item());
        list.Last().IsSelected = true;
        _vm.Items = new ObservableCollection<ViewModel.Item>(list);
        listBox.ScrollIntoView(list.Last());
    }
}

Debug - Performance Profiler - Application Timeline... подождите немного, нажмите кнопку, подождите немного, закройте окно. Вы увидите 2 макета с VirtualizingStackPanel. Моя цель - иметь только один, и я не знаю, как это сделать.

Проблема с воспроизведением заключается в том, чтобы имитировать нагрузку (при создании ListViewItem стоит дорого), но я надеюсь, что это более четко продемонстрирует проблему сейчас.

4b9b3361

Ответ 1

Методы прокрутки обычно не очень хорошо работают на VirtualizingStackPanel. Чтобы обойти это, я использую следующее решение.

  • Поверните VirtualizingStackPanel. Пойдите с обычной StackPanel для шаблона панели.
  • Сделать внешний слой вашего DataTemplate здесь LazyControl: http://blog.angeloflogic.com/2014/08/lazycontrol-in-junglecontrols.html
  • Убедитесь, что вы установили высоту на этом LazyControl.

Я обычно получаю хорошие результаты из этого подхода. Чтобы он выполнял именно то, что вы просите, вам может потребоваться добавить дополнительную логику в LazyControl, чтобы дождаться, когда будет установлен какой-либо флаг (после вызова метода прокрутки).