I was previously using the code behind to manually add items to my ListBox, but it was terribly slow. I heard that data binding via XAML was the way to go, in terms of performance.
So I managed to get the data binding working (new to binding), but to my dismay, the performance is no better than my previous non-data binding method.
The idea is that my ListBox contains an Image with a name below it. I did some benchmarking and 54 items take a full 8 seconds to display. Which naturally is too long for a user to wait.
The source images are at a maxiumum: 2100x1535px and range from 400kb>4mb per file.
The images required to reproduce this issue can be found here: Link removed as question has been answered and my server doesn't have very much bandwidth allowance. Other image source here: https://i.stack.imgur.com/iS0h0.jpg
I've made a reproducible example of the issue below. What am I doing wrong that is making this so slow?
Thank you.
The XAML:
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800" WindowState="Maximized">
<Grid>
<ListBox x:Name="listBoxItems" ItemsSource="{Binding ItemsCollection}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel>
<Image Width="278" Height="178">
<Image.Source>
<BitmapImage DecodePixelWidth="278" UriSource="{Binding ImagePath}" CreateOptions="IgnoreColorProfile" />
</Image.Source>
</Image>
<TextBlock Text="{Binding Name}" FontSize="16" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
</VirtualizingStackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
The code behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
internal class Item : INotifyPropertyChanged
{
public Item(string name = null)
{
this.Name = name;
}
public string Name { get; set; }
public string ImagePath { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ObservableCollection<Item> ItemsCollection;
List<Item> data;
public MainWindow()
{
InitializeComponent();
this.data = new List<Item>();
this.ItemsCollection = new ObservableCollection<Item>();
this.listBoxItems.ItemsSource = this.ItemsCollection;
for (int i = 0; i < 49; i ++)
{
Item newItem = new Item
{
ImagePath = String.Format(@"Images/{0}.jpg", i + 1),
Name = "Item: " + i
};
this.data.Add(newItem);
}
foreach (var item in this.data.Select((value, i) => new { i, value }))
{
Dispatcher.Invoke(new Action(() =>
{
this.ItemsCollection.Add(item.value);
}), DispatcherPriority.Background);
}
}
}
}