Click here to Skip to main content
Email Password   helpLost your password?
Title:       A C# thumbnail control using background worker
Author:      Sreejai R. Kurup 
Email:       sreejai@hotmail.com
Environment: C# .NET 2.0
Keywords:    Control, Imaging, Thumbnail, Background worker
Level:       Intermediate
Description: A C# based thumbnail viewer derived from the ListView control
Section      Miscellaneous
SubSection   General

JThumbnailView Demo project

Introduction

Many a times we come across situations where it is required to show images from a directory as thumbnails. This control JThumnailView exactly does that. The additional feature of this control is that it uses a background worker to load the images asynchronously.

Background

It all started when I wanted to send some photos back home electronically. Of course, you could do that using e-mails, however, when doing so mostly it would be zipped and sent. When I saw my parents struggling to keep track of where they saved the photos for later view, I realised that I needed to provide them something which could be controlled by me. Hence, I started developing an application which once installed on a machine will serve as an automatic picture downloader and a basic viewer. Thus, I was faced with the following challenges:

  1. How to do an automatic download? My answer came in the form of the edtFTPnet component, with which I could go and download file(s) from an FTP server. However, this posed another problem, everytime I want to send new photos, I needed some mechanism which would transparently do it without any manual intervention. That is when I decided to use an XML file which the application would read and perform the necessary action. However, once I started developing the XML file, I wanted to encrypt it and give a different extension. Hence, I used a DESCryptoServiceProvider to perform the encryption/decrytion for the file. Now, all I have to do is send my file (which has a *.pfi) extension and give a password. When this file is double clicked, it will ask for a password and it will start downloading the photos automatically from my FTP site.
  2. Now the second challenge for me to provide a simple photo viewer in the same application so that the user need not worry about where the photos are stored. For this, I needed two components, one Explorer-like folder viewer and a thumbnail viewer. That is when I got the WindowsExplorer component written by Rajesh Lal. Though I had to make little modifications to suit my requirements, it saved me a lot of time.
  3. The only thing remaining was a thumbnail viewer. I searched for a good one, but most of them were for ASP.NET. Finally I saw one, but again it was based on VC++. That is when I decided to write one. I made it a control so that people can re-use it. This article describes the JThumnailView component I created for this purpose. For those who want the full version of the application, please contact me by email. I will be more than happy to provide it (though it is still in the beta phase!!!).

Using the code

The component is very simple to understand. It has been derived from the standard ListView control so that I need not worry about things like scrolling, sorting etc. :).

The following are the main published properties:

public int ThumbNailSize; //default value: 95
By default, it has a value of 95 (Windows explorer seemed to use this size, so I made it as default)
public int ThumbBorderColor; //default value: Color.Wheat

Set this if you want the thumbnails to have a different border.
public string FolderName; //default value: Application folder
This is the directory from which the thumbnails are to be loaded. The component has another property CanLoad which should be set to true to load the images. This should be set to true on the constructor of the form.

One of the main problems with most of the thumbnail viewers is that if the number of files in a directory are high, then the viewer takes a lot of time to load. To avoid this, the technique I am using is to create a Default thumbnail which is just a square of the thumbnail size and initially set this as the imageindex for all the items. This will give the user an impression that the thumbnails have been loaded. Now the actual thumbnails are loaded in the background using a BackgroundWorker.
private BackgroundWorker myWorker = new BackgroundWorker();
The loading of the items is handled in the method: ReloadItems()

private void ReLoadItems() 
{ 
    BeginUpdate(); 
    Items.Clear(); 
    LargeImageList.Images.Clear(); 
    AddDefaultThumb(); 
    
    string strFilter = "*.jpg;*.png;*.gif;*.bmp"; 
    List fileList = new List(); 
    string[] arExtensions = strFilter.Split(';'); 
    
    foreach (string filter in arExtensions) 
    { 
        string[] strFiles = Directory.GetFiles(folderName, filter); 
        fileList.AddRange(strFiles); 
        for (int i = 0; i < strFiles.Length; i++) 
        { 
            FileInfo fiTemp = new FileInfo(strFiles[i]); 
            Items.Add(fiTemp.Name, 0).Tag = strFiles[i]; } 
        } 
        
        fileList.Sort(); 
        EndUpdate(); 
        if (myWorker != null) { myWorker.RunWorkerAsync(fileList); } 
    }
}
The AddDefaultThumb() method creates a default thumbnail as mentioned above. The actual thumbnails are drawn in the DoWork() event of the BackgroundWorker. The thumbnails are drawn using the usual graphic methods.

Points of Interest

Definitely the use of the BackgroundWorker improved the efficiency of the viewer considerably. Another interesting thing was the use of the PixelOffSetMode and InterpolationMode of the Graphics object. When I drew the thumbnails first, I could not match the clarity of the images in the Windows Explorer thumbnail. That is when I tried out the various PixelOffSetModes and the InterpolationModes. I achieved the desired result with the following combination:

    PixelOffsetMode = PixelOffsetMode.None;
   
    InterpolationMode = InterpolationMode.HighQualityBicubic;  
    

History

Version 1.1

Thanks to all the valuable comments, I have modified the component to include some more checks. :). I have also modified the demo to include a minimal picture viewer. Thanks once again to all of us especially Michael for his special interest and feedback. I still have not really tested dynamic dispose and creation and hence not sure whether the problem is still there.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralAbout the source code
Cisily
0:30 13 May '09  
i am a new c# learner, and now i am did something about the thumbnails,and i think your project was good ,i'd like learn something from it . So i hope the source code .
thanks .
Mailto: nichoish@sohu.com
GeneralStill inconsistency with the thumbnail and the filename
martinmoesbypetersen
1:24 10 Jan '09  
Really usefull control - I love it, thax.

But I still see some inconsistencies between the thumbnail and the file it represents....

Try this one for size :

1) Add the control and a trackbar slider to a form.
2) Set the foldername to a folder with +50 images
3) Sync the trackbar control value to the ThumbnailSize property
4) Make the control reload everytime the Thumbnail size is changed...

Result :
On every refresh of the Thumbnail list, the image index doesn't seem to be reset resulting in items getting the false images index of the ImageList.... and it seems to fit with the index which was the active index, before the reload was called....

I cannot seem to find the problem as everything checks out - could someone pls give me a hint?
GeneralThumbnail Generation
Member 3525863
23:09 17 Dec '08  
Thanks for the sample given.
I need a structure like showing "n" number of folders(which contain images) in one panel and selecting one among those, would show you the images as thumbnails in another panel.
I have done usingImagelist and picturebox controls, but it is time consuming while loading.
How to reduce loading thumbnails and folders.

Thanks In Advance
Vijay
vbr_dotnet@yahoo.co.in
GeneralMy vote of 1
Wertugo555
22:53 8 Dec '08  
Коновалы вы господа. Look at Adobe Photo Stoke. Bad
GeneralGreat Work!!
Richard Blythe
20:11 10 Oct '08  
I've been looking for a good article that addresses this issue.
Super job! 5 from me.

I've used up all my sick days, so today I'm calling in dead.

Generalfull version of the application
Member 3682548
20:55 3 Aug '08  
Hi Sreejai,
I would like to get full version of the application. Mainly I am interested on how to write a windows Explorer like form with thumbnail view.

Thanks
Dush
GeneralError
mfmanca
10:57 30 Oct '07  
I've used this nice control on a pc with VS2005 and VS2008 installed and everything worked fine.
When I moved the EXE and the DLL files onto another pc I got an error when launching the EXE

EventType : clr20r3 P1 : jthumbnaildemo.exe P2 : 1.0.0.0
P3 : 464ec32f P4 : jaicontrols P5 : 1.0.2695.24022 P6 : 464ec16c
P7 : c P8 : 10 P9 : system.io.directorynotfound

What's missing on that pc?
Does this control need a certain .NET framework installed?

Thanks



Maurizio
GeneralRe: Error
Edward Ceballos
23:41 29 May '08  
Very Rare Error I just create a folder [ProgramFiles]\Microsoft Visual Studio 9.0\Common7\IDEImages
Works Fine!!!!
?????

Edward Ceballos

Generalgetting filename of selected items
Elsys
19:05 19 Oct '07  
im having trouble getting the filename of the selected items in the contorl while the normal listview control seems to work. How might I do this?
GeneralStop BackGroundWorker
fracasado
8:56 31 Aug '07  
Hi I need to stop BackGroundWorker.
I use your code in my app, but I allow the user to edit an image.
When it happend I open with a Process my paint App.
So I set the Process.WaitForExit.
When I return to my app, I want to reload all the images but it only reload the images that haven't load yet.
Could you help me?
Thanks
GeneralRe: Stop BackGroundWorker
Sreejai R. Kurup
19:30 31 Aug '07  
1. Do you edit an image after all of them have been loaded?
2. It would be better if you could modify the component so that only the edited picture is re-loaded. If you want my help, please send me your source. I can try it out.
3. For the time being, you can explicitly call the 'ReloadItems()' method after returning to the program.

Thanks
Sreejai
Generalcan this be modded to work for mpeg thumbnails ?
UltraWhack
8:09 31 May '07  
Great stuff. I would be interested to see your amended project Michael. Please can you email it to me ultrawhack @ yahoo.ca

I am very interested to see this work for mpegs by showing me thumbnails of all avis/mpegs/movs/qt in a given directory.

Another feature that would be great would be to be able to set the frame from the mpeg that the thumbnail is taken from...
GeneralRe: can this be modded to work for mpeg thumbnails ?
Sreejai R. Kurup
19:03 31 May '07  
Hi UltraWhack,

Most of the modifications which Mike had pointed out (including a function to load from a string array) has already been included in the latest uploaded version. However, I will try to work on the mpeg option.

Thanks
Sreejai
GeneralRe: can this be modded to work for mpeg thumbnails ?
UltraWhack
3:22 1 Jun '07  
There is a commercial activex out there that does mpeg thumbnails with choice of frame that is displayed on thumbnail.

http://www.viscomsoft.com/products/mithumbnail/

Its functionality can perhaps be imitated/improved on.

Here's some other interesting code snippets on this topic
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=458385&SiteID=1
http://www.devcity.net/Articles/219/1/article.aspx

GeneralBug: Thumbnail image inconsistency with real image
mpgjunky
7:06 17 May '07  
Hi,

First of all, thanks for the control, great job. I wanted to write something identical for a project, but you've saved me the hassle. Smile

However, unless you are already aware of it, you have a bug in that thumbnails displayed do not correspond to the real images (including in your demo program). To clarify, your thumbnail showed image from b.jpg, but the tag was a.jpg (I hope I have explained myself correctly Smile ).

The problem is with:

foreach (string filter in arExtensions)
{
string[] strFiles = Directory.GetFiles(folderName, filter);
fileList.AddRange(strFiles);
for (int i = 0; i < strFiles.Length; i++)
{
FileInfo fiTemp = new FileInfo(strFiles[i]);
Items.Add(fiTemp.Name, 0).Tag = strFiles[i];
}
}

fileList.Sort();
EndUpdate();
if (myWorker != null)
{
myWorker.RunWorkerAsync(fileList);
}

...you are adding the items from strFile[] which is in a certain order, but the thumnails are generated using fileList which is a sorted list.

The correct logic would be something like:

List fileList = new List();
string[] arExtensions = strFilter.Split(';');

foreach (string filter in arExtensions)
{
string[] strFiles = Directory.GetFiles(folderName, filter);
fileList.AddRange(strFiles);
}

fileList.Sort();

for (int i = 0; i < fileList.Count; i++)
{
FileInfo fiTemp = new FileInfo(fileList[i]);
Items.Add(fiTemp.Name, 0).Tag = fileList[i];
}

EndUpdate();

...basically get files, add files to the List, sort the List, and then add the Items based on the order of the [sorted] List. This will ensure the correct thumbnail will shown for the correct tag name (or filename).

(PS: The above fix may not be necessarily the most efficient, but it works Smile ).
(PS: I have added a new method LoadItems(string[] _filelist) in case you (or anyone else is interested) - which creates thimbnails from an array of filenames).

Cheers,
Michael.
GeneralCrashes if you make it start loading new thumbnails before it's finished
mrhaan
8:40 11 Apr '07  
You should probably check what's going on with the BackgroundWorker before asking it to RunWorkerAsync.

I easily got the demo project to crash with these steps:
1.   Click the browse button.
2.   Choose a directory with enough images that it will take some time to load all the thumbnails.
3.   Immediately click the browse button again, and quickly choose another directory.
4.   If step 3 was completed soon enough after step 2, the application crashes with a "This BackgroundWorker is currently busy and cannot run multiple tasks concurrently" error.
GeneralBackgroundWorker
BushRob
13:11 27 Mar '07  
Hi,

It appears that the Background worker process continues to run even after the thumbnails are finished being populated. Have you come across this and modified your code since to prevent this from happening?

Thanks.
GeneralRe: BackgroundWorker
Sreejai R. Kurup
20:48 4 Apr '07  
Hi,

Could you please explain why you said that the process continues to run? Once all the thumbnails are loaded, it calls the RunWorkerCompleted method correctly.

Regards
Sreejai
GeneralRe: BackgroundWorker
wmhp1
13:15 20 Apr '07  
Hi

Well I had a similar problem (well I still do Wink ).

I'm using your listview in a custom user control. If I dispose of the custom user control while the images are loading, then straight after create a new instance of the custom user control and display it, you get an error.

"This BackgroundWorker is currently busy and cannot run multiple tasks concurrently."
// If I do this while images are still loading, you'll get an error.
splitContainer1.Panel2.Controls.Remove(objCustomControl);

objCustomControl = null;
objCustomControl = new SlideShowCtrl();

objCustomControl.Dock = DockStyle.Fill;
splitContainer1.Panel2.Controls.Add(objCustomControl);

objCustomControl.thumbList.FolderName = "Some file";

I think the problem is, when the backgroundworker is running it doesn't get disposed or stoped when the listview is destroyed.

I'll try and see if I can fix my problem, but help will definitely be appreciated!!!Smile Smile
AnswerRe: BackgroundWorker [modified]
mpgjunky
7:18 17 May '07  
You can fix the problem easily by using:

if(myWorker.IsBusy)
myWorker.CancelAsync();

Obviously, you need to insert the above code before the control is requested to re-run, i.e. myWorker.RunWorkerAsync(fileList), and in the constructor add myWorker.WorkerSupportsCancellation = true.

Regards,
Michael.


-- modified at 12:34 Thursday 17th May, 2007
GeneralNo source code
cykophysh39
22:53 24 Feb '07  
Hi,
Your solution sounds interesting but sadly no source code is attached.

Kind Regards,
Gary

My Website || My Blog || My Articles

GeneralRe: No source code
Sreejai R. Kurup
1:01 25 Feb '07  
Oops...Don't know why it didn't get uploaded to the server. Will do it immediately.

Thanks


Last Updated 19 May 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010