У меня есть ListView
(виртуализация включена по умолчанию), которая ItemsSource
привязана к ObservableCollection<Item>
.ScrollIntoView и ListView с виртуализацией
Когда данные заселены (свойство установлено и добавлено уведомление) Я вижу 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
is expensive), но я надеюсь, что это более четко продемонстрирует проблему сейчас.
Вы всегда хотите отобразить последний элемент после того, как коллекция элементов была добавлена в пользовательский интерфейс? Он не решает проблему двух пропусков макета, но если это так, вы можете изменить способ сортировки элементов перед добавлением их в пользовательский интерфейс, чтобы элемент, который вы хотите отобразить, - это первый элемент в коллекции – Bijington
@Bijington, коллекция уже отсортирована (по дате, не показана в mcve), я не должен менять порядок. Это может быть любой элемент (в реальном проекте это multi-select 'ListView', прокрутка к последнему выбранному элементу).В идеале я ищу способ хранения/восстановления состояния ListView в приложении MVVM (выбор обрабатывается, но позиция прокрутки отсутствует, это * как-то * обрабатывается с помощью 'ScrollIntoView'), что двойная де виртуализация - это своего рода XY- проблема (но я в порядке с 'ScrollIntoView', проблема - только де-виртуализация). – Sinatr
Я принял столько же, но я подумал, что стоит спросить. Это, безусловно, звучит как интересная проблема для решения. Вы должны указать «ListView» это положение прокрутки, прежде чем загружать контент. Не могли бы вы как-то подклассифицировать «ListView» и предотвратить рендеринг, пока не закончите пропуск нагрузки (возможно, флаг IsLoadingContent), чтобы вы могли назначить элементы, а также отметить, какой элемент нужно выбрать и ввести в поле зрения? – Bijington