具有虚拟化功能的ScrollIntoView和ListView

时间:2016-07-11 09:14:12

标签: c# wpf listview mvvm virtualization

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

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

我的理解是:

  1. ListView通过绑定加载数据,并为屏幕上的项创建ListViewItem,从索引0开始。
  2. 然后我致电listView.ScrollIntoView()
  3. 现在ListView第二次(创建ListViewItem s)。
  4. 如何防止去虚拟化发生两次(我不想在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个布局传递。我的目标是只有一个,我不知道如何。

    repro的问题是模拟加载(创建ListViewItem is expensive时),但我希望现在能更清楚地证明这个问题。

1 个答案:

答案 0 :(得分:-1)

Scroll方法通常不能在VirtualizingStackPanel上运行良好。为了解决这个问题,我使用以下解决方案。

  1. 抛弃VirtualizingStackPanel。使用正常的StackPanel作为面板模板。
  2. 从此处将DataTemplate的外层设为LazyControl:http://blog.angeloflogic.com/2014/08/lazycontrol-in-junglecontrols.html
  3. 确保在LazyControl上设置高度。
  4. 我通常会从这种方法中获得良好的表现。为了使它完全符合您的要求,您可能需要向LazyControl添加一些额外的逻辑以等待设置某个标志(在您调用scroll方法之后)。