Click here to Skip to main content
15,881,172 members
Articles / Programming Languages / C#
Tip/Trick

A Simple Thumbnail Viewer Control

Rate me:
Please Sign up or sign in to vote.
3.86/5 (3 votes)
3 Jul 2014CPOL1 min read 26.1K   1.4K   10   7
A very simple Thumbnail Viewer inherited from FlowLayoutPanel for beginners

Image 1

Introduction

This control allows you to add Thumbnail by dragdrop image from desktop and preview the Image by double-clicking on Thumbnail.

Using the Code

At first, declare an "ImageExtensions" List to make sure all files added to Control are Image Type.

C#
/// <summary> Image Extensions accepted by this control
        /// </summary>
        private List<string> ImageExtensions = new List<string> 
        { ".JPG", ".JPE", ".BMP", ".GIF", ".PNG" };

The DragDrop system is just simple, use DragEnter event to set an effect when user drags files to control and DragDrop event to deal with the files.

C#
 void ThumbnailViewerControl_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }

void ThumbnailViewerControl_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                AddImage(files);
            }
        }

When dropped files to control, the AddImage method will add image to List as binary and then show a thumbnail on Panel by MakeThumbnail method.

C#
public void AddImage(string[] files)
        {
            this.Cursor = Cursors.WaitCursor;

            byte[] binary;
            for (int i = 0; i < files.Count(); i++)
            {
                // Only accept Image files
                if (ImageExtensions.Contains(Path.GetExtension(files[i]).ToUpperInvariant()))
                {
                    // Convert Image File to Binary
                    binary = File.ReadAllBytes(files[i]);

                    // Add binary data to List
                    ImageList.Add(binary);

                    // Create a Thumnail of Image and add Thumbnail to Panel
                    MakeThumbnail(binary);

                    GC.GetTotalMemory(true);
                }
            }

            this.Cursor = Cursors.Default;
        }

public void MakeThumbnail(byte[] binary)
        {
            // Create a Picture Box for showing thumbnail image
            PictureBox thumb = new PictureBox();
            thumb.MaximumSize = new Size(128, 128);
            thumb.MinimumSize = new Size(128, 128);
            thumb.Size = new Size(128, 128);
            thumb.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            thumb.SizeMode = PictureBoxSizeMode.Zoom;

            // Create a border when Mouse entered
            thumb.MouseEnter += new EventHandler(thumb_MouseEnter);

            // Clear border when Mouse leaved
            thumb.MouseLeave += new EventHandler(thumb_MouseLeave);

            // Preview image when Mouse Double Clicked
            thumb.DoubleClick += new EventHandler(thumb_DoubleClick);

            // Set thumbnail image
            MemoryStream ms = new MemoryStream();
            thumb.Image = Image.FromStream(new MemoryStream(binary))
                .GetThumbnailImage(thumb.Width - 2, thumb.Height - 2, null, new IntPtr());
            ms.Close();

            // Add to Panel
            this.Controls.Add(thumb);
        }

Now, thumbnails are added to Panel, but to make it more prettier when user moves mouse cursor over it, use MouseEnter event to draw a border around thumbnail and clear border when Mouse leaves by MouseLeave event.

C#
void thumb_MouseLeave(object sender, EventArgs e)
        {
            ((PictureBox)sender).Invalidate();
        }

void thumb_MouseEnter(object sender, EventArgs e)
        {
            var rc = ((PictureBox)sender).ClientRectangle;
            rc.Inflate(-2, -2);
            ControlPaint.DrawBorder(((PictureBox)sender).CreateGraphics()
                , ((PictureBox)sender).ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
            ControlPaint.DrawBorder3D(((PictureBox)sender).CreateGraphics()
                , rc, Border3DStyle.Bump);
        }

Thumbnail is small, right? So when user wants to "preview" full size image, let's show them a preview form created on thumbnail's mouse doubleclick event.

C#
void thumb_DoubleClick(object sender, EventArgs e)
        {
            Form previewForm = new Form();
            previewForm.FormBorderStyle = FormBorderStyle.SizableToolWindow;
            previewForm.MinimizeBox = false;
            previewForm.Size = new System.Drawing.Size(800, 600);
            previewForm.StartPosition = FormStartPosition.CenterScreen;

            PictureBox view = new PictureBox();
            view.Dock = DockStyle.Fill;

            int index = this.Controls.GetChildIndex((PictureBox)sender);
            view.Image = BinaryToImage(ImageList[index]);

            view.SizeMode = PictureBoxSizeMode.Zoom;

            previewForm.Controls.Add(view);
            previewForm.ShowDialog();
        }

On the event above, when doubleclicked on thumbnail, we must convert the binary data back to an Image, this method will do that job.

C#
public static Image BinaryToImage(byte[] binaryData)
       {
           if (binaryData == null) return null;
           byte[] buffer = binaryData.ToArray();
           MemoryStream memStream = new MemoryStream();
           memStream.Write(buffer, 0, buffer.Length);
           return Image.FromStream(memStream);
       }

That's all !!! This is just a tip for beginners, I recommend you add more methods to it like:

  • Delete Image method: Delete a selecting thumbnail, be careful with Panel control index and List item index.
  • BackgroundWorker: The UI will freeze when you add a large number of images, so a backgroundworker will handle it.
  • Threading: Large size image will consuming time. Try something like Parallel looping to make the process faster.

Thanks for reading and feel free to comment.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Junior)
Vietnam Vietnam
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThank you for this!! Pin
bernardlawes30-Jan-23 6:29
bernardlawes30-Jan-23 6:29 
GeneralMy vote of 1 Pin
Fabrice CARUSO4-Jul-14 6:54
Fabrice CARUSO4-Jul-14 6:54 
GeneralRe: My vote of 1 Pin
Nguyen.H.H.Dang4-Jul-14 7:31
professionalNguyen.H.H.Dang4-Jul-14 7:31 
GeneralRe: My vote of 1 Pin
Fabrice CARUSO4-Jul-14 11:06
Fabrice CARUSO4-Jul-14 11:06 
There R the reasons why I voted :

- There are too many articles like yours on codeproject. Furthermore, what's the need for a beginner article when we can find a lot of serious articles about the same ?? Ok, sorry.... I dont wanna be bad with you so I'm gonna explain things I'd change if I had to develop that kind of things.

- Don't use "((PictureBox)sender).CreateGraphics()" : It's the worst way to do that. (at least, keep the Graphics to reuse it and dispose it when U R finished ). What's the good way ? Instead : inherit from PictureBox and override OnPaint, OnMouseEnter, OnMouseLeave + use your PictureBox. Better than that : create you own Control where you also handle youself the image drawing ( using Graphics.DrawImage ).

- If you want a really fast drawing : dont use a FlowLayoutPanel (which is hwnd based) and don't use PictureBoxes (which are hwnd based) for each image -> Have only one control -> Your thumb viewer. There you can handle your image collection, your layout, your drawing, your mouse events... Then you can also try to load pictures in a thread only when they are visible for the first time... (Then you will see that - I don't know why - even in a thread, GDI+ Image.FromStream or Image.FromFile hangs the UI thread for a few milliseconds, making impossible to have smooth scrolls when loading... Unless you don't use GDI+ (which is complex))

- If you want optimum performance (considering you are using winforms, not wpf nor directx), resize your pictures with GDI+ (and antialiasing) before you store them (for better memory usage), and maybe consider to display them using GDI ( not gdi+. Using BitBlt or StretchBlt, plus SetStrechBltMode with STRETCH_DELETESCANS )

- Don't store Full Sized images in an "ImageList". This is a mistake for your memory ! If you need to load an image in another place (to see it fullscreen or in another control) store the path instead and load only when u need it.

- What's the use for "MemoryStream ms = new MemoryStream();" ??? You don't use ms !!!! Before posting, clean your code...

That's all....
GeneralRe: My vote of 1 Pin
Nguyen.H.H.Dang4-Jul-14 11:36
professionalNguyen.H.H.Dang4-Jul-14 11:36 
GeneralRe: My vote of 1 Pin
Fabrice CARUSO4-Jul-14 22:20
Fabrice CARUSO4-Jul-14 22:20 
GeneralRe: My vote of 1 Pin
Nguyen.H.H.Dang4-Jul-14 23:37
professionalNguyen.H.H.Dang4-Jul-14 23:37 

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.