ListView 혹은 ListBox를 이용한 섬네일 만드는 클래스 입니다.
class ThumbnailView : ListView
class ThumbnailBox : 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);
}
}
}'C# > Windows Form' 카테고리의 다른 글
| [C# Windows Form] ZipFile 다루기 - 압축 및 해제 (0) | 2025.10.06 |
|---|---|
| [C# Windows Form] 비동기 Custom Form 종료, 파일 열기, 프린트 등 창 닫기 (BlockingCollection - queue) (0) | 2025.09.08 |
| [C# Windows Form] Drag & Drop 기능 사용하기 (파일, 폴더 불러오기) (1) | 2025.08.15 |
| [C# Windows Form] Tray 아이콘 잔상 제거하기 (Win32 API 사용) (3) | 2025.07.29 |
| [C# Windows Form] Win32 API EnumWindows 를 사용한 프로그램 타이틀 가져오기 (웹 브라우저 따라다니기) (1) | 2025.07.28 |












