【发布时间】:2016-07-11 09:14:11
【问题描述】:
我有ListView(虚拟化默认开启),ItemsSource 绑定到ObservableCollection<Item> 属性。
填充数据时(设置属性并发出通知)我在分析器中看到 2 个布局峰值,第二个发生在调用 listView.ScrollIntoView() 之后。
我的理解是:
-
ListView通过绑定加载数据并为屏幕上的项目创建ListViewItem,从索引 0 开始。 - 然后我打电话给
listView.ScrollIntoView()。 - 现在
ListView第二次执行此操作(创建ListViewItems)。
如何防止这种去虚拟化发生两次(我不希望在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 的布局通道。我的目标是只有一个,但我不知道怎么做。
repro的问题是模拟负载(创建ListViewItemis expensive时),但我希望现在能更清楚地证明问题。
【问题讨论】:
-
您是否总是希望在将项目集合添加到 UI 后显示最后一个项目?它不能解决两个布局传递的问题,但如果是这种情况,您可能会在将项目添加到 UI 之前更改项目的排序方式,以便您想要显示的项目是集合中的第一个项目
-
@Bijington,集合已经排序(按日期,未显示在 mcve 中),我不应该更改顺序。它可以是任何项目(在实际项目中它是多选
ListView,滚动到最后选择的项目)。理想情况下,我正在寻找在 MVVM 应用程序中存储/恢复ListView状态的方法(选择已处理,但滚动位置未处理,它以某种方式由ScrollIntoView处理),即双重去虚拟化是一种 XY 问题(但我对ScrollIntoView没意见,问题只有两次去虚拟化)。 -
我假设了很多,但我认为值得一问。这听起来确实是一个有趣的问题。在以某种方式加载其内容之前,您需要告诉
ListView它的滚动位置。您能否以某种方式子类化ListView并阻止渲染,直到您完成加载传递(可能是 IsLoadingContent 标志),以便您可以分配项目并标记需要选择并显示哪些项目? -
如果将ListBox的可见性设置为隐藏,然后在调用ScrollIntoView后使其可见,会解决问题吗?
标签: c# wpf listview mvvm virtualization