0

I recently discover how to do task in background and try to use these in WPF for a test.

What I try to test is to create a picture carousel in a picture box.

To do this I read this, this and this and that's what I have :

    public partial class Page2 : Page
    {
        public Thread backgroundcaroussel;
        public Page2()
        {
            InitializeComponent();
            backgroundcaroussel = new Thread(ImgFlip);
            backgroundcaroussel.IsBackground = true;
            backgroundcaroussel.Start();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            backgroundcaroussel.Abort();
            MainWindow.Fenetre.Content = MainWindow.pgUn;
        }

        private void ImgFlip()
        {
        Again:

            this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (ThreadStart)delegate ()
            {
                BitmapSource btmSrc1 = Imaging.CreateBitmapSourceFromHBitmap(Properties.Resources._1080p_1.GetHbitmap(),
                        IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                img_moins.Source = btmSrc1;
            });
            Thread.Sleep(2000);

            this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,(ThreadStart)delegate ()
            {
                BitmapSource btmSrc2 = Imaging.CreateBitmapSourceFromHBitmap(Properties.Resources._1080p_2.GetHbitmap(),
                        IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                img_moins.Source = btmSrc2;
            });
            Thread.Sleep(2000);

            this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (ThreadStart)delegate ()
            {
                BitmapSource btmSrc3 = Imaging.CreateBitmapSourceFromHBitmap(Properties.Resources._1080p_3.GetHbitmap(),
                        IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                img_moins.Source = btmSrc3;
            });
            Thread.Sleep(2000);
            goto Again;
        }

    }

When I use this code, the memory usage won't stop increase and reach 1 or 2 Go (before I stop it). I don't think that's normal :)

I also read this,this and this to fix the problem but don't clearly know what to do.

How to solve this memory consumption ? Do I use the right methodology ?

Thomas_LCP
  • 53
  • 7
  • Doing this in a Thread that sleeps most of the time is horrible. Use a timer instead, preferably a DispatcherTimer. But thanks for showing us that `goto` is still there. You should read an introductory C# book and then perhaps one about WPF. – Clemens Jan 28 '21 at 08:08
  • Be aware that although the Tick event of a DispatcherTimer is invoked in the UI thread, you may still declare the Tick handler method `async` and run a background Task that creates a BitmapSource. – Clemens Jan 28 '21 at 08:13
  • 1
    You are calling [Bitmap.GetHbitmap](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.gethbitmap) method a lot. If you look at the docs, you can find a small remark: _"**YOU** are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object"_ – vasily.sib Jan 28 '21 at 08:16
  • As another note, you would typically not load bitmaps from `System.Drawing.Bitmap` resources in `Properties.Resources`. Instead, add image files to the Visual Studio project, set their Build Action to Resource and load BitmapImages from Assembly Resource Pack URIs. See e.g. here: https://stackoverflow.com/a/22957974/1136211 – Clemens Jan 28 '21 at 08:16
  • please do ***not*** use goto unless you absolutely ***have to***. in your case, you don't have to. it does the same as a `while(true){}`, only smellier. – Franz Gleichmann Jan 28 '21 at 08:16
  • Finally it is entirely unclear why you think you need any background processing at all. You could simply preload a set of BitmapSources and cyclically assign them to the Source property of an Image. – Clemens Jan 28 '21 at 08:19
  • I agree. The problem I have is I'm a beginner and self-taught man and I don't know what to search to do what I want :) Perhaps do you have a solution for the memory usage ? – Thomas_LCP Jan 28 '21 at 08:22
  • Let us know if you are interested in how to do this in an acceptable way, or if for some reason you want to stick with Thread, Sleep, Invoke, CreateBitmapSourceFromHBitmap and all that. – Clemens Jan 28 '21 at 08:47
  • Of course i'm interested ! I love learn ! As I wrote, I try to know if I use the correct methodology :) – Thomas_LCP Jan 28 '21 at 08:50

1 Answers1

2

The problem with increasing memory consumption in your code is that you have to delete the handle returned by GetHbitmap by calling DeleteObject, e.g. as explained here: https://stackoverflow.com/a/5827468/1136211


Besides that, in a WPF application you would implement the whole logic in a completely different way.

You would not use bitmap resources in Properties/Resources.resx but instead just add the image files to your Visual Studio project, e.g. in a project folder named "Images". Then set the Build Action of those files Resource as e.g. shown here: https://stackoverflow.com/a/12693661/1136211

On startup, load all those image resources into a list. Then start a DispatcherTimer that cyclically assigns them to the Source property of an Image element.

public partial class MainWindow : Window
{
    private readonly List<ImageSource> images = new List<ImageSource>();
    private int imageIndex;

    public MainWindow()
    {
        InitializeComponent();

        images.Add(new BitmapImage(new Uri("pack://application:,,,/Images/Image1.png")));
        images.Add(new BitmapImage(new Uri("pack://application:,,,/Images/Image2.png")));
        images.Add(new BitmapImage(new Uri("pack://application:,,,/Images/Image3.png")));

        ShowNextImage();

        var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
        timer.Tick += TimerTick;
        timer.Start();
    }

    private void TimerTick(object sender, EventArgs e)
    {
        ShowNextImage();
    }

    private void ShowNextImage()
    {
        image.Source = images[imageIndex];
        imageIndex = (imageIndex + 1) % images.Count;
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268