我有ListView(默认情况下处于虚拟化状态),该ItemsSource绑定(bind)到ObservableCollection<Item>属性。

当填充数据(设置属性并发出通知)时,我在探查器中看到2个布局峰值,第二个发生在调用listView.ScrollIntoView()之后。

我的理解是:

  • ListView通过绑定(bind)加载数据,并从索引0开始为屏幕上的项目创建ListViewItem
  • 然后我叫listView.ScrollIntoView()
  • 现在ListView做了第二次(创建ListViewItem)。

  • 如何防止非虚拟化发生两次(我不想在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());
        }
    }
    

    调试-Performance Profiler-应用程序时间表...稍等片刻,单击按钮,稍等片刻,关闭窗口。您将看到2个带有VirtualizingStackPanel的布局通行证。我的目标是只有一个,但我不知道该怎么做。

    repro的问题是模拟负载(在创建ListViewItem is expensive时),但我希望现在可以更清楚地演示该问题。

    最佳答案

    Scroll方法通常在VirtualizingStackPanel上效果不佳。要解决此问题,我使用以下解决方案。

  • 抛弃VirtualizingStackPanel。使用普通的StackPanel作为面板模板。
  • 从此处使DataTemplate的外层成为LazyControl:http://blog.angeloflogic.com/2014/08/lazycontrol-in-junglecontrols.html
  • 确保您在该LazyControl上设置了高度。

  • 通过这种方法,我通常可以获得良好的性能。为了使其完全符合您的要求,可能需要向LazyControl添加一些其他逻辑以等待设置一些标志(在调用scroll方法之后)。

    10-04 16:19