我有一个
我的理解是:
我尝试使用
xaml:
cs:
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());
}
}
调试 - 性能分析器 - 应用程序时间轴...等待一会儿,点击按钮,再等一会儿,关闭窗口。您将看到使用 VirtualizingStackPanel
的 2 次布局传递。我的目标是只有一次,但我不知道如何做。
重现问题的困难之处在于模拟负载(当创建 ListViewItem
是昂贵的操作),但我希望现在更清楚地展示了问题。
ListView
,滚动到最后选择的项目)。理想情况下,我正在寻找在MVVM应用程序中存储/恢复ListView
状态的方法(选择已处理,但滚动位置没有,它是通过ScrollIntoView
某种方式 处理的),这种双重去虚拟化是一种XY问题(但我对ScrollIntoView
没问题,只是两次去虚拟化是个问题)。 - SinatrListView
内容之前告诉它滚动位置。你可以尝试通过子类化ListView
,在完成加载过程之前阻止渲染(可能需要一个IsLoadingContent标志),这样你就可以分配项目,并标记哪个项目需要被选中并显示出来。 - Bijington