ListView 혹은 ListBox를 이용한 섬네일 만드는 클래스 입니다.

class ThumbnailView : ListView

class ThumbnailBox : ListBox

상속을 통해 만들었고 필요한 함수는 커스텀해서 사용 가능합니다.

 

ListView는 가로로도 확장이 가능하고,

ListBox는 세로만 가능하게 만들었고, 사이즈가 가변적으로 변경됩니다.

 

먼저 결과 및 사용 방법.

ListView, ListBox 베이스 섬네일

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //폴더에 있는 이미지 파일 불러오기
        private void button_add_Click(object sender, EventArgs e)
        {
            string folder = @"C:\Test"; //폴더 경로
            string[] files = Directory.GetFiles(folder);

            button_add.Enabled = false; //추가 버튼

            Task.Run(() =>
            {
                int viewCount = thumbnailView1.GetCount(); //View Item 개수
                int boxCount = thumbnailBox1.GetCount(); //Box Item 개수

                foreach (string file in files)
                {
                    thumbnailView1.AddThumbnail(file, viewCount++.ToString("D4")); //View에 섬네일 추가
                    thumbnailBox1.AddThumbnail(file, boxCount++.ToString("D4")); //Box에 섬네일 추가
                }

                Invoke((MethodInvoker)delegate
                {
                    button_add.Enabled = true; //추가 버튼
                });
            });
            
            //GetSelectedPathList, RemoveSelectedThumbnail, RemoveAllThumbnail 있음.
        }
    }
}

 

ListView 클래스 (빌드 후 사용)

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1 //네임스페이스 맞춰줄 것.
{
    public class ThumbnailView : ListView
    {
        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

        private int thumbnailWidth = 128;
        private int thumbnailHeight = 128;

        public ThumbnailView()
        {
            this.View = View.LargeIcon;
            this.MultiSelect = true; //여러 장 선택
            this.OwnerDraw = false; //기본 그리기 사용
            this.HideSelection = false; //포커싱 잃으면 선택 취소

            ImageList imageList = new ImageList();
            imageList.ColorDepth = ColorDepth.Depth32Bit;
            imageList.ImageSize = new Size(thumbnailWidth, thumbnailHeight); // 원하는 썸네일 크기
            this.LargeImageList = imageList;
        }

        protected override void OnHandleCreated(EventArgs e) //깜박임 방지
        {
            base.OnHandleCreated(e);
            SendMessage(this.Handle, 0x1000 + 54, 0x00010000, 0x00010000); // LVS_EX_DOUBLEBUFFER
        }

        public int GetCount() //현재 개수 구하기
        {
            return this.Items.Count;
        }

        public void AddThumbnail(string path, string name) //섬네일 추가
        {
            using (Image original = Image.FromFile(path))
            {
                Image thumbnail = MakeThumbnail(original, thumbnailWidth, thumbnailHeight);
                int index = this.LargeImageList.Images.Count;

                InvokeAuto(() =>
                {
                    this.LargeImageList.Images.Add(thumbnail);
                    ListViewItem item = new ListViewItem();
                    item.Text = name; //섬네일 이름
                    item.ImageIndex = index; //새로운 인덱스
                    item.Tag = path; // 원본 경로 저장 (object형태 tag에 저장)
                    this.Items.Add(item);
                });
            }
        }

        private Image MakeThumbnail(Image image, int width, int height) //섬네일 생성
        {
            Bitmap bitmap = new Bitmap(width, height);
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                g.Clear(Color.White);
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

                //이미지 비율 유지
                double ratio = Math.Min((double)width / image.Width, (double)height / image.Height);
                int newWidth = (int)(image.Width * ratio);
                int newHeight = (int)(image.Height * ratio);

                int x = (width - newWidth) / 2;
                int y = (height - newHeight) / 2;

                g.DrawImage(image, x, y, newWidth, newHeight); //섬네일 그리기
                g.DrawRectangle(Pens.Gray, 0, 0, width - 1, height - 1); //테두리 그리기
            }

            return bitmap;
        }

        private void InvokeAuto (Action action) //스레드 사용 대비
        {
            if (InvokeRequired)
            {
                Invoke((MethodInvoker)delegate
                {
                    action();
                });
            }
            else
            {
                action();
            }
        }

        public List<string> GetSelectedPathList() //선택 된 경로 반환
        {
            if (this.SelectedIndices == null)
            {
                return new List<string>();
            }

            List<string> pathList = new List<string>();

            foreach (int index in this.SelectedIndices)
            {
                pathList.Add(this.Items[index].Tag as string);
            }

            return pathList;
        }

        public void RemoveSelectedThumbnail() //선택 된 요소 삭제
        {
            if (this.SelectedIndices == null || this.SelectedIndices.Count <= 0)
            {
                return;
            }

            this.BeginUpdate();

            //뒷쪽부터 삭제
            for (int i = this.SelectedIndices.Count - 1; i >= 0; --i)
            {
                int index = this.SelectedIndices[i];
                Remove(index);
            }

            this.EndUpdate();
        }

        public void RemoveAllThumbnail() //모든 요소 삭제
        {
            if (this.Items.Count <= 0)
            {
                return;
            }

            this.BeginUpdate();

            for (int i = this.Items.Count - 1; i >= 0; --i)
            {
                Remove(i);
            }

            this.EndUpdate();
        }

        private void Remove(int index)
        {
            this.Items.RemoveAt(index);

            this.LargeImageList.Images[index].Dispose();
            this.LargeImageList.Images.RemoveAt(index);
        }
    }
}

 

 

ListBox 클래스

 

※ ItemHeight가 변경 되면 공백이 남는 경우가 생깁니다.

이에따라 고정크기 panel 하위에 listbox를 생성하고, 현재 보여지는 크기에 맞추어 ItemHeight가 가변적으로 변경됩니다.

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

namespace WindowsFormsApp1
{
    class ThumbnailBox : Panel
    {
        private class ThumbnailInfo
        {
            public Image NormalImage { get; set; }
            public Image FocusImage { get; set; }
            public string Path { get; set; }
            public string Name { get; set; }
        }

        private readonly Color highlighColor = Color.FromArgb(80, 0, 120, 215);

        private readonly int thumbnailMaxSize = 128; //최대 사이즈 고정 (255넘으면 섬네일 생성 실패)
        private readonly int thumbnailSize = 128;

        private readonly int imageFixedHeight = 110;
        private readonly int textHeight = 30;
        private readonly int margin = 3;

        private List<ThumbnailInfo> thumbnailInfoList = new List<ThumbnailInfo>();
        private ListBox listBox = new ListBox();

        public ThumbnailBox()
        {
            //리스트박스 사이즈가 가변일 수 있어 panel하위에서 크기 체크.
            this.Controls.Add(listBox);
            this.SizeChanged += panel_SizeChanged;
            this.BackColor = Color.White;
            this.BorderStyle = BorderStyle.FixedSingle;
            this.Margin = new Padding(0);

            listBox.Margin = new Padding(0);
            listBox.BackColor = Color.Gray;
            listBox.Dock = DockStyle.Fill;
            listBox.DrawMode = DrawMode.OwnerDrawFixed; //직접 그려주기
            listBox.DrawItem += ThumbnailBox_DrawItem; //직접 그려주기
            listBox.ItemHeight = imageFixedHeight; //아이템 높이 설정
            listBox.BorderStyle = BorderStyle.None;
            listBox.SelectionMode = SelectionMode.MultiExtended; //다중 선택
        }

        private void panel_SizeChanged(object sender, EventArgs e)
        {
            int panelHeight = this.ClientSize.Height; //실제 표시 가능한 영역

            int visibleCount = (int)Math.Round((double)panelHeight / imageFixedHeight); //패널 높이에 따라 표시할 아이템 개수 결정
            visibleCount = Math.Max(visibleCount, 1); //최소 1개 보장

            listBox.ItemHeight = panelHeight / visibleCount; //꽉 차게 분배
            listBox.Invalidate();
        }

        public int GetCount() //현재 개수 구하기
        {
            return listBox.Items.Count;
        }

        public void AddThumbnail(string path, string name) //섬네일 추가
        {
            try
            {
                ThumbnailInfo thumbnailInfo = new ThumbnailInfo
                {
                    Path = path,
                    Name = name,
                };

                using (Image image = Image.FromFile(path))
                {
                    int width = image.Width;
                    int height = image.Height;

                    double ratio = (double)Math.Min(thumbnailSize, thumbnailMaxSize) / (double)Math.Max(width, height);

                    int newWidth = (int)(width * ratio);
                    int newHeight = (int)(height * ratio);

                    //섬네일 생성
                    Image thumbnail = image.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero);
                    thumbnailInfo.NormalImage = thumbnail;

                    //선택 시 하이라이트 섬네일 생성
                    Bitmap bitmap = new Bitmap(thumbnail.Width, thumbnail.Height);
                    using (Graphics g = Graphics.FromImage(bitmap))
                    {
                        g.DrawImage(thumbnail, 0, 0, bitmap.Width, bitmap.Height);

                        using (Brush brush = new SolidBrush(highlighColor))
                        {
                            g.FillRectangle(brush, 0, 0, bitmap.Width, bitmap.Height);
                        }
                    }
                    thumbnailInfo.FocusImage = bitmap;
                }

                thumbnailInfoList.Add(thumbnailInfo);

                //이름으로 아이템 추가
                if (InvokeRequired)
                {
                    Invoke((MethodInvoker)delegate
                    {
                        listBox.Items.Add(name);
                    });
                }
                else
                {
                    listBox.Items.Add(name);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        private void ThumbnailBox_DrawItem(object sender, DrawItemEventArgs e)
        {
            e.DrawBackground();

            int index = e.Index;
            if (index < 0)
            {
                return;
            }

            //일반 or 선택 시 백그라운드 및 이미지
            Brush background = Brushes.White;
            Image image = thumbnailInfoList[index].NormalImage;
            int itemHeight = listBox.ItemHeight;

            if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) //선택 된 상태
            {
                background = Brushes.DodgerBlue;
                image = thumbnailInfoList[index].FocusImage;
            }

            //배경 그리기
            e.Graphics.FillRectangle(background, e.Bounds.X + margin, e.Bounds.Y + margin, e.Bounds.Width - margin * 2, e.Bounds.Height - margin);

            //이미지 그리기
            e.Graphics.DrawImage(image, e.Bounds.Left + margin, e.Bounds.Top + margin, e.Bounds.Width - margin * 2, itemHeight - textHeight);

            //텍스트 그리기
            using (StringFormat stringFormat = new StringFormat() { Alignment = StringAlignment.Center }) //텍스트 가운데 정렬
            {
                e.Graphics.DrawString(thumbnailInfoList[index].Name,
                                      e.Font, //폰트
                                      Brushes.Black, //색상
                                      (e.Bounds.Left + e.Bounds.Right) / 2, //위치x
                                      e.Bounds.Top + itemHeight - textHeight + 10, //위치y
                                      stringFormat);
            }

            //선택 시 테두리
            e.DrawFocusRectangle();
        }

        public List<string> GetSelectedPathList() //선택 된 경로 반환
        {
            if (listBox.SelectedIndices == null)
            {
                return new List<string>();
            }

            List<string> pathList = new List<string>();

            foreach (int index in listBox.SelectedIndices)
            {
                pathList.Add(thumbnailInfoList[index].Path);
            }

            return pathList;
        }

        public void RemoveSelectedThumbnail() //선택 된 요소 삭제
        {
            if (listBox.SelectedIndices == null || listBox.SelectedIndices.Count <= 0)
            {
                return;
            }

            listBox.BeginUpdate();
            //뒷쪽부터 삭제
            for (int i = listBox.SelectedIndices.Count - 1; i >= 0; --i)
            {
                int index = listBox.SelectedIndices[i];
                Remove(index);
            }

            listBox.EndUpdate();
            listBox.Refresh();
        }

        public void RemoveAllThumbnail() //모든 요소 삭제
        {
            if (listBox.Items.Count <= 0)
            {
                return;
            }

            listBox.BeginUpdate();

            //뒷쪽부터 삭제
            for (int i = listBox.Items.Count - 1; i >= 0; --i)
            {
                Remove(i);
            }

            listBox.EndUpdate();
            listBox.Refresh();
        }

        private void Remove(int index)
        {
            listBox.Items.RemoveAt(index);

            //이미지 리소스 해제 필요
            thumbnailInfoList[index].NormalImage.Dispose();
            thumbnailInfoList[index].FocusImage.Dispose();
            thumbnailInfoList.RemoveAt(index);
        }
    }
}

+ Recent posts