question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Displaying an Image from a byte[] in an ItemControl leads to huge memory leak

See original GitHub issue
  • .NET Core Version: 6.0 or any other .NET core version I could try
  • Windows version: 10
  • Does the bug reproduce also in WPF for .NET Framework 4.8?: Yes

Problem description: I need to display an image which is in a form of byte[] (array of byte) in a WPF ItemsControl, but everything I could try leads to memory leak. I use what I think to be the canonical MVVM approach to do so. To enable the Image to display the byte[], I convert it in the ViewModel into an ImageSource using a MemoryStream (example below). I’ve tried some more exotic ways to get this byte[] converted to an ImageSource, using Bitmap and GDI+ in between. But this always end up with the same conclusion: the memory fills in and never gets freed.

Minimal repro:

MainWindow.xaml

<Window x:Class="Toy.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:Toy"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <Button Content="Collect Garbage" Click="gcButton_Click"/>
            <Button x:Name="wxButton" Click="wxButton_Click"/>
            <ItemsControl x:Name="wxToy" ItemsSource="{Binding}" HorizontalAlignment="Center">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Border BorderBrush="Bisque" Width="100" Height="100" BorderThickness="2">
                            <Image Source="{Binding Data}"/>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Toy
{


    internal class ToyItem : INotifyPropertyChanged
    {
        #region Boilerplate INotifyPropertyChanged

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            PropertyChanged?.Invoke(this, e);
        }

        public void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler? PropertyChanged;

	#endregion

	/// <summary>
	/// Build an image from Picture bytes
	/// </summary>
	/// <param name="imageData">Picture as array of bytes</param>
	/// <returns>Pictures as BitmapImage</returns>
	public static ImageSource? BitmapFromRaw(byte[]? imageData)
	{
		if (imageData == null) return null;
		var image = new BitmapImage();
		var mem = new MemoryStream(imageData);
		image.BeginInit();
		//image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
		//image.CacheOption = BitmapCacheOption.None;
		//image.UriSource = null;
		image.StreamSource = mem;
		image.EndInit();
		//mem.Close();
		//mem.Dispose();
		image.Freeze();
 		return image;
	}

	public ImageSource? Data
        {
            get { return _Data; }
            set
            {
                if (value != _Data)
                {
                    _Data = value;
                    OnPropertyChanged();
                }
            }
        }
        private ImageSource? _Data;
        
        public ToyItem ()
        {
            Data = BitmapFromRaw(Properties.Resources.pexels_jonathan_faria_8581946);
        }

    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        ObservableCollection<ToyItem> ToyList = new ObservableCollection<ToyItem>();

        int Counter = 0;

        public MainWindow()
        {
            InitializeComponent();

            wxToy.DataContext = ToyList;
            wxButton.Content = Counter;
        }

        private void gcButton_Click(object sender, RoutedEventArgs e)
        {
            GC.Collect();
        }

        private void wxButton_Click(object sender, RoutedEventArgs e)
        {
            wxButton.Content = ++Counter;

            ToyList.Clear();

            for (int i = 0; i < 5; i++)
            {
                ToyList.Add(new ToyItem());
            }
        }
    }

}

You could see some attempts to rweak the code with the comments //

References

You can download a ready-to-use solution of this here

I’ve also ask the question here This is to apply in the Desktop application I’m developping here The problem seems similar but is different to #1082 because it needs byte[] and ItemsControl.

Additional notes

This example uses a resource (Properties.Resources.pexels_jonathan_faria_8581946) to get an image in byte[]. This is only for simplifying the example, don’t bother to explain how I could workaround the problem by using a jpg image and URI instead. In the real application, I have no choice but getting the images from byte[] arrays…

If you run this code, you could hit a button to generate and show 5 images at a time in a MVVM scheme in an ItemsControl, just the way we are suppose to do it in WPF/C# I guess… Clicking the button several times will bring this deadly simple code to progressively eat Gigabytes of memory.

I really do hope this is not a bug and that I’m doing something wrong here. Please point out on what I’m doing bad here…

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
Starwercommented, Feb 9, 2022

Thanks @lindexi and @gurpreet-wpf ! I really value your responsive reply with a pointer to a potential solution.

However, the #2397 trick is not really something addressing a pure MVVM approach… In my example, I only control the View Model contrary to the other issue where it instantiates the controls directly in the code. I doubt we could call that a duplicate then.

Anyway, I’ll try this trick on my side and let you know if this solves the issue (and how I made it into a MVVM). If this works, that’s a workaround I can use in my application (and maybe that could unblock other people who face the issue) so it would at least solve the use-case.

We keep in touch !

1reaction
gurpreet-wpfcommented, Feb 9, 2022

@Starwer I hope we can mark this as duplicate of #2397 and close it?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Display Image from Byte Array in WPF - Memory Issues
I realise that as a user hovers over bitmap sources are generated and the memory isn't freed up, I've tried giving RealImage a...
Read more >
Memory leak when using RasterImageViewer in ItemsControl
It binds a List to an ItemsControl and displays each image via a DataTemplate that contains a RasterImageViewer.
Read more >
C# WPF BitmapSource Memory Leak
That said, there is an issue with creating BitmapSource objects from streams. The byte[] held by the stream is not being freed when...
Read more >
Question - Memory leak?
I am using the memory profiler, and it says i have almost 3gigs of system.byte, which seems insane cause my game is quite...
Read more >
[Vb.Net]-BeginInvoke Memory Leak?
There is no longer any reason to call Delegate.BeginInvoke() . And, the word Synchronized in the names of the delegate and the method...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found