简体   繁体   中英

WPF ObservableCollection slow performance

I am new in WPF and read a lot about performance issues. I have some issues on some program with a lot of styles using mah apps and other 3rd party controls. So I created a simple test. Please see the code above. This code works fine. Takes about 1 second to load. Changing the for loop to 1,000 items, will take about 2-3 seconds and 10,000 items about 8 seconds to load. I don't understand why. I configured virtualziation that means to draw only the visible rows. It should take the same time for initialization. I don't have any style or heavy controls. All the WPF mvvm concept is very helpful but I can't use it if I have a lot of performance issues.

UPDATE The ListView loads 10,000 items fast (up to 1 sec), the problem is the items control. How can I overcome this? in my real application I should use the item control. I can see in the profiler that wihout the ItemsControl the memory is stable around 166MB and with the ItemsControl is always increasing. I can't understandm according to the articles ListView and ListBox are derived from ItemsControl .

View Model:

public ViewModel()
        {
            for(int i = 0; i < 100; i ++)
            {
                Collection.Add(new Person()
                {
                    FirstName = $"FirstName{i}",
                    LastName = $"LastName{i}",
                    Gender = $"Gender{i}",
                });
            }
            Title = "Title";
            IsVisible = true;
        }

        public ObservableCollection<Person> Collection { get; } = new ObservableCollection<Person>();

        private string _title;
        public string Title
        {
            get { return _title; }
            set 
            { 
                if(_title != value) 
                {
                    _title = value;
                    OnPropertyChanged();
                } 
            }
        }


        private bool _isVisible;
        public bool IsVisible
        {
            get { return _isVisible; }
            set
            {
                if (_isVisible != value)
                {
                    _isVisible = value;
                    OnPropertyChanged();
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        // Create the OnPropertyChanged method to raise the event
        // The calling member's name will be used as the parameter.
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

Window:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance local:ViewModel}"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <DataTemplate x:Name="ItemTemplate" DataType="{x:Type local:Person}">
            <Grid ScrollViewer.VerticalScrollBarVisibility="Visible"
                  VirtualizingPanel.IsVirtualizing="True">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <TextBlock Margin="3,5,6,0" Text="{Binding FirstName}" Background="Red"/>
                <TextBlock Margin="3,5,6,0" Text="{Binding LastName}" Background="Yellow"/>
                <TextBlock Margin="3,5,6,0" Text="{Binding Gender}" Background="Beige"/>
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <StackPanel>
        <ListView ItemsSource="{Binding Collection}" Height="250" ScrollViewer.VerticalScrollBarVisibility="Auto"
                  VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Margin="3,5,6,0" Text="{Binding FirstName}" Background="Blue"/>
                        <TextBlock Margin="3,5,6,0" Text="{Binding LastName}" Background="Yellow"/>
                        <TextBlock Margin="3,5,6,0" Text="{Binding Gender}" Background="Beige"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
            <ItemsControl
                ItemsSource="{Binding Collection}"
                ItemTemplate="{Binding ItemTemplate}"
                VirtualizingPanel.IsVirtualizing="True">
            </ItemsControl>
    </StackPanel>
</Window>

Update 2

After the comment to avoid using StackPanel (without height), I tried to use only the ItemsControl with fix height and without any panel (only a TextBlock ). Now it takes 5 seconds instead of 8 seconds.

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance local:ViewModel}"
        Title="MainWindow" Height="450" Width="800"
        Loaded="Window_Loaded">
    <Window.Resources>
        <DataTemplate x:Name="ItemTemplate" DataType="{x:Type local:Person}">
                <TextBlock Margin="3,5,6,0" Text="{Binding FirstName}" Background="Blue"/>
        </DataTemplate>
    </Window.Resources>
        <ItemsControl
                ItemsSource="{Binding Collection}"
                Height="250"
                VirtualizingPanel.IsContainerVirtualizable="True"
                VirtualizingPanel.IsVirtualizing="True"
                VirtualizingPanel.CacheLengthUnit="Item">
        </ItemsControl>
</Window>

blame StackPanel. It tells ItemsControl it can use infinite Height, so no virtualization. You don't have problem with ListView, because it has explicit Height="250".

A simple fix is to use Grid (and not row Height="Auto", it will result in the same behavior):

<Window ...>
 <Grid>
   <Grid.RowDefinitions>
    <RowDefinition Height="*"/>
    <RowDefinition Height="*"/>
   </Grid.RowDefinitions>

   <ListView Grid.Row="0" ...>

   <ScrollViewer Grid.Row="1">
     <ItemsControl  ...>
   </ScrollViewer>
 </Grid>
</Window>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM