1

My task is to create an array of thumbnails of images from a folder in a C# Windows Forms control. The number of images is unknown, so I use an instance of FlowLoayoutPanel to arrange them. Then I create a number of instances of PictureBox and assign the thumbnails of all the images to them, like in the following code (obvious action omitted):

FlowLayoutPanel flp = new FlowLayoutPanel();
foreach (string fileName in fileNames)
{
    PictureBox pb = new PictureBox();
    pb.Image = Image.FromFile(fileName).GetThumbnailImage(pb.Height, pb.Width, null, new IntPtr());
    flp.Controls.Add(pb);
}

This code functions, but very slowly: it takes many seconds to display a hundred images or so, even in Release mode. I also tried to assign the images themselves, instead of creating thumbnails, but it is still very slow.

Is there any possibility to accelerate the creation of thumbnails or to achieve the desired result (an album-like collection of thumbnails) in a different way (e.g. in an external C routine to add via Interop)?

  • 1
    You could put this in a background worker then invoke to the FlowLayoutPanel (since its on the UI thread for sure) to add the image Something to help you this (the principle is the same) https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread – Camadas Jun 21 '22 at 12:09
  • 6
    Every time you add an item you are refreshing the FlowLayoutPanel which takes time. You can use AddRange to add all the images at end : flp.Controls.AddRange(put array of images here); – jdweng Jun 21 '22 at 12:18
  • 1
    How much speed up you will get, if you will (temporary) remove `Controls.Add()` and `new PictureBox()` calls and leave only thumbnail creation? And may be storing thumbnail images into somewhat like a `List()`? And in any way it's a really good idea to use `Controls.AddRange` as @jdweng suggested. – Serg Jun 21 '22 at 12:22
  • @Camadas Thanks, I tried to make the method async, but it did not accelerate much. – Stanislav Koncebovski Jun 21 '22 at 12:30
  • @jdweng Thanks a lot, a great and simple idea. Going to try it at once. – Stanislav Koncebovski Jun 21 '22 at 12:30
  • @Serg Thakns, see my answer to jgweng – Stanislav Koncebovski Jun 21 '22 at 12:31
  • 1
    That may have to do with what @jdweng told I would say loading images should always be on another thread besides the UI thread, since while its loading (like you told could load hundred's) it will block the UI thread. You can do on a new thread, to load all imagens then use the AddRange and pass them at that time – Camadas Jun 21 '22 at 12:37
  • Either I am creating thumbnails in a loop and add them in picture boxes consequently to the FlowLayoutPanel, or I create all the thumbnails and then add them using AddRange(), the summary computation time is roughly the same (obviously). Only if it s done in a worker thread, the process looks better for the user, I agree. – Stanislav Koncebovski Jun 21 '22 at 12:43
  • 1
    Either in memory (e.g. Dictionary) or on disk (Dictionary) you should create a cache for the thumbnail images. Whenever you change the list of filenames you first try to get the needed images from the cache. If an entry is missing in the cache, start a new background task that will (maybe in parallel) create all missing thumbnails and updates the cache. When done, refresh your picture boxes again from the cache. – Oliver Jun 21 '22 at 13:09
  • @Oliver Yes, the idea with the cache is obvious, but how to accelerate the thumbnails for the first time? – Stanislav Koncebovski Jun 21 '22 at 13:15
  • I have no ready-to-use solution, but I can suggest to check out PLINQ: `var thumbnails = fileNames.Select(x=>Image.FromFile(x)).AsParallel().AsOrdered().Select(x=>x.GetThumbnailImage(100, 100, null, IntPtr.Zero)); var imgBoxes = thumbnails.Select(x=> new PictureBox(){Image = x}).ToArray(); Controls.AddRange(imgBoxes);` – Serg Jun 21 '22 at 13:48
  • Use a Task to enqueue the thumbnails in a ConcurrentQueue (or a ConcurrentDictionary). Dequeue in the UI Thread (sometimes it's just about *perception*). Double-buffer the FlowLayoutPanel using a custom Control (`SetStyle(ControlStyles.OptimizedDoubleBuffer, true);` in the Constructor). -- Handle the cached objects with care, or you're going to leak as nobody ever leaked before. – Jimi Jun 21 '22 at 13:51
  • The other suggestion will be to completely change the approach to avoid user inconvenience due to the slow thumbnails. For example on the form startup you can fill the list with the hardcoded stub image instead of thumbnails (this will allow user to start working immediately) and then load thumbnails in the background and update UI item-by-item, or with batches or as a whole (you will need to make some measurements and select the option which will not prevent user from doing their work during such an update due to the UI freezes). – Serg Jun 21 '22 at 13:56

0 Answers0