'Improve WPF performance with many usercontrols

I am building my first WPF application and I ran into some performance issues that I am not sure how to deal with. I have a list of around 500 items that I bind to UserControls using an ItemsControl in a Page. The data gets loaded into memory during the program startup and is displayed when the user opens the page. This already takes a few seconds but can be dealt with, however the data can also be reordered (e.g. ascending, descending) and filtered which takes around 0.6-1.3 seconds every time (depending on reordering method used). It seems to be caused by the UI parsing and layout and I was hoping this could be improved somehow. Here is the performance graph in case that helps.

And here is the code of the Item class and associated UserControl xaml (simplified):

public class Item : INotifyPropertyChanged
    {
        public String name { get; set; }
        public String content { get; set; }
        public BitmapImage image { get; set; }
        
        public event PropertyChangedEventHandler PropertyChanged;
        private Visibility visibility = Visibility.Visible;
        
        protected void OnPropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        
        public void LoadImage()
        {
            var bitmap = new BitmapImage();
            bitmap.BeginInit();
            bitmap.DecodePixelWidth = 48;
            bitmap.DecodePixelHeight = 48;
            bitmap.UriSource = new Uri(somepath);
            bitmap.EndInit();
            bitmap.Freeze();
            image = bitmap;
        }
        
        public Item()
        {
            LoadImage();
        }
    }
<UserControl 
    ...
    Visibility="{Binding Path=Visibility}">

    <Grid Background="White"> 
        <Image x:Name="ItemImage" Source="{Binding Path=image}" Width="64" Height="64"></Image>
        <TextBlock x:Name="NameTextBlock" Text="{Binding Path=name}" FontSize="18px"></TextBlock>
        <TextBlock x:Name="ContentTextBlock" Text="{Binding Path=content}" FontSize="14px" ></TextBlock>
    </Grid>
</UserControl>

And here is the xaml and code of the Page which displays the Usercontrols:

<Page>
    <StackPanel x:Name="RootStackPanel">
        <Some static content>

        <ScrollViewer x:Name="ItemScrollViewer" CanContentScroll="True" VerticalScrollBarVisibility="Auto" Width="auto" VerticalAlignment="Top" HorizontalAlignment="Stretch">
            <ItemsControl x:Name="CustomItemsControl">
            </ItemsControl>
        </ScrollViewer>
    </StackPanel>
</Page>
public partial class ItemsPage : Page
    {
        public static ItemsPage Instance = new ItemsPage();
        
        public ObservableCollection<Item> collection { get; set; }
        
        // called by a button on the page
        private void ReorderItems()
        {
            // Simplified: Usually more ordering options e.g. OrderByDescending
            List<Item> temp = new List<Item>(collection.OrderBy(item => item.name));
            collection.Clear();
            foreach (Item item in temp)
            {
                collection.Add(item);
            }
        }
        
        public ItemsPage()
        {
            InitializeComponent();

            collection = new ObservableCollection<Item>(ItemCollection.GetItems()); 
            // getItems returns the list of items as List<>

            FrameworkElementFactory factoryPanel = new FrameworkElementFactory(typeof(VirtualizingStackPanel));
            factoryPanel.SetValue(VirtualizingStackPanel.IsItemsHostProperty, true);
            factoryPanel.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, true);
            factoryPanel.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Recycling);
            ItemsPanelTemplate template = new ItemsPanelTemplate();
            template.VisualTree = factoryPanel;
            CustomItemsControl.ItemsPanel = template;

            FrameworkElementFactory dataTemplateFactory = new FrameworkElementFactory(typeof(ItemUserControl));
            DataTemplate dataTemplate = new DataTemplate();
            dataTemplate.VisualTree = dataTemplateFactory;
            CustomItemsControl.ItemTemplate = dataTemplate;
            
            CustomItemsControl.ItemsSource = collection;
        }
    }

As can be seen I tried using Virtualization but I am not sure if I am using it correctly since I saw no measurable performance gain. The pictured reordering method is obviously not ideal since I alternatively tried reordering the members of the items instead and calling the PropertyChangedEventHandler on the bound members. This did improve performance by getting rid of the parsing step but still takes 600ms on average per reorder. Are there better methods to do that or should I be trying to simplify my UserControls instead?



Solution 1:[1]

When you use a ScrollViewer around ItemsControl Virtualization will not work. extract ItemsControl from ScrollViewer.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ehsan.Soltani