我有ListView
(虚拟化默认开启),其中ItemsSource
绑定到ObservableCollection<Item>
属性。ScrollIntoView和带虚拟化的ListView
当数据填充(属性设置和通知提高)时,我在profiler中看到2个布局尖峰,第二个在通话listView.ScrollIntoView()
后发生。
我的理解是:
ListView
加载数据通过结合和画面上的项目,从指数0- 开始然后我叫
listView.ScrollIntoView()
创建ListViewItem
。 - 现在
ListView
第二次做(创建ListViewItem
s)。
如何防止发生两次去虚拟化(我不希望在发生ScrollIntoView
之前发生)?
我试图使用ListBox
作出repro。
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个布局通行证。我的目标是只有一个,我不知道如何。
repro的问题是模拟负载(创建时ListViewItem
is expensive),但我希望现在更清楚地表明问题。
一旦项目集合已添加到UI,您是否总是希望将最后一项放入视图中?它不能解决两个布局传递的问题,但如果是这种情况,您可能会在将项目添加到UI之前更改项目的排序方式,以便使要显示的项目成为集合中的第一个项目 – Bijington
@Bijington,集合已经排序(按日期,未在mcve中显示),我不应该改变顺序。它可以是任何项目(在实际项目中它是多选'ListView',滚动到最后选择的项目)。理想情况下,我正在寻找在MVVM应用程序中存储/恢复ListView状态的方法(选择被处理,但滚动位置不是,它用* ScrollIntoView处理),双重去虚拟化是XY-问题(但我对'ScrollIntoView'确定,只有两次解除虚拟化是问题)。 – Sinatr
我尽量多的,但我认为这是值得一问。这听起来像是一个有待解决的有趣问题。你需要告诉'ListView'它以某种方式加载它的内容之前它的滚动位置。你能否以某种方式子类化ListView并阻止渲染,直到完成加载传递(也许是IsLoadingContent标志),以便您可以分配项目并标记需要选择哪些项目并将其展示到视图中? – Bijington