Click here to Skip to main content
15,354,006 members
Home / Discussions / WPF
   

WPF

 
QuestionWpf System.Windows.Media.ImageSourcesConverter cannot be applied Pin
StealthRT1-Feb-22 16:08
MemberStealthRT1-Feb-22 16:08 
AnswerRe: Wpf System.Windows.Media.ImageSourcesConverter cannot be applied Pin
Richard Deeming1-Feb-22 21:49
mveRichard Deeming1-Feb-22 21:49 
GeneralRe: Wpf System.Windows.Media.ImageSourcesConverter cannot be applied Pin
StealthRT2-Feb-22 2:39
MemberStealthRT2-Feb-22 2:39 
GeneralRe: Wpf System.Windows.Media.ImageSourcesConverter cannot be applied Pin
Richard Deeming2-Feb-22 2:44
mveRichard Deeming2-Feb-22 2:44 
Question(beginner) There must be a better way (TextBlock properties) Pin
Maximilien26-Jan-22 5:08
MemberMaximilien26-Jan-22 5:08 
AnswerRe: (beginner) There must be a better way (TextBlock properties) Pin
Richard Deeming26-Jan-22 5:33
mveRichard Deeming26-Jan-22 5:33 
GeneralRe: (beginner) There must be a better way (TextBlock properties) Pin
Maximilien26-Jan-22 9:10
MemberMaximilien26-Jan-22 9:10 
QuestionConverting a byte[] to a ImageSource means huge memory leak Pin
Starwer24-Jan-22 11:42
MemberStarwer24-Jan-22 11:42 
Hi Geeks !

I bang my head on what looked to be a ridiculously trivial problem...

In a sentence: I need to display a image which is in a form of byte[] (array of byte) in a WPF ItemsControl.

'Sounds simple enough right ? I could find tens of solutions on the web, all similarly making use of a MemoryStream assigned to an BitmapImage. See snippet bellow in C# 10:

public static ImageSource? BitmapFromRaw(byte[]? imageData)
{
    if (imageData == null) return null;
    var image = new BitmapImage();
    var mem = new MemoryStream(imageData);
    //mem.Position = 0;
    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;
}


At first it looks like it works fine. The images converted this way could be displayed in an Image WPF control. BUT: this leads to a big memory leak. Looking at the memory usage in Visual Studio it shows that neither the BitmapImage nor the MemoryStream get freed from memory. From the lines commented out, you could see I've already tried to tweak this with different options... no success.

Here is the smallest code I could make to reproduce the problem:

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);
			//mem.Position = 0;
			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);
        }

        ~ToyItem()
        {
            //if (Data == null) return;
            //var image = (BitmapImage)Data;
            //image.StreamSource.Close();
            //image.StreamSource.Dispose();
        }

    }

    /// <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 can download a ready-to-use solution of this here

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.

Please help me ! What am I doing wrong ?! D'Oh! | :doh:

EDIT: Problem has been solved
I eventually posted a bug report to the WPF GitHub: Displaying an Image from a byte[] in an ItemControl leads to huge memory leak.
They kindly indicated that this was a known problem referenced in Known issus: WPF Image memory leak when remove image from visual tree.

There is actually a workaround to that problem, but nothing you could do yet to solve this in a proper MVVM scheme.

In a few words, changing the code of wxButton_Click as below will fix the memory leak:
private void wxButton_Click(object sender, RoutedEventArgs e)
{

    // Memory leak Fix
    for (int i = 0; i < wxToy.Items.Count; i++)
    {
        var uiElement = (FrameworkElement)wxToy.ItemContainerGenerator.ContainerFromIndex(i);
        uiElement.DataContext = null;
        uiElement.UpdateLayout();
    }

    wxButton.Content = ++Counter;

    ToyList.Clear();

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

}



Full story is there: Displaying an Image from a byte[] in an ItemControl leads to huge memory leak.

Thanks to all the coders who replied to this post !

modified 10-Feb-22 1:42am.

AnswerRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Gerry Schmitz24-Jan-22 16:15
mveGerry Schmitz24-Jan-22 16:15 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Starwer24-Jan-22 19:58
MemberStarwer24-Jan-22 19:58 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Gerry Schmitz24-Jan-22 21:26
mveGerry Schmitz24-Jan-22 21:26 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Starwer25-Jan-22 7:56
MemberStarwer25-Jan-22 7:56 
AnswerRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Richard Deeming25-Jan-22 21:51
mveRichard Deeming25-Jan-22 21:51 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Starwer26-Jan-22 9:10
MemberStarwer26-Jan-22 9:10 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Gerry Schmitz27-Jan-22 6:33
mveGerry Schmitz27-Jan-22 6:33 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Starwer27-Jan-22 20:29
MemberStarwer27-Jan-22 20:29 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
lmoelleb3-Feb-22 4:58
Memberlmoelleb3-Feb-22 4:58 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Starwer5-Feb-22 0:28
MemberStarwer5-Feb-22 0:28 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
lmoelleb6-Feb-22 3:27
Memberlmoelleb6-Feb-22 3:27 
AnswerRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Starwer30-Jan-22 5:04
MemberStarwer30-Jan-22 5:04 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Gerry Schmitz30-Jan-22 5:23
mveGerry Schmitz30-Jan-22 5:23 
GeneralRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Starwer30-Jan-22 7:45
MemberStarwer30-Jan-22 7:45 
SuggestionRe: Converting a byte[] to a ImageSource means huge memory leak Pin
Richard Deeming30-Jan-22 21:52
mveRichard Deeming30-Jan-22 21:52 
Question(MahApp) not using style for control Pin
Super Lloyd19-Jan-22 23:07
MemberSuper Lloyd19-Jan-22 23:07 
AnswerRe: (MahApp) not using style for control Pin
Super Lloyd19-Jan-22 23:08
MemberSuper Lloyd19-Jan-22 23:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.