using System;
using System.IO;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string folderPath = Path.Combine(Directory.GetCurrentDirectory(), "targetFolder");
            string[] files = Directory.GetFiles(folderPath);

            DateTime now = DateTime.Now.AddDays(-3); //3일 전 파일까지 검출용.
            foreach (string file in files)
            {
                FileInfo fileInfo = new FileInfo(file);

                //A < B = 1, A == B = 0, A > B = -1
                if (DateTime.Compare(now, fileInfo.LastWriteTime) > 0) //기준보다 오래된 파일 삭제
                {
                    fileInfo.Delete();
                }
            }
        }
    }
}

 

폰트 적용

 

예제로 창원 단감 폰트 사용..!

ChangwonDangamAsac-Bold_0712.ttf

using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Text;
using System.IO;

namespace ApplyFontFromCode
{
    public partial class Form1 : Form
    {
        private PrivateFontCollection privateFontCollection;

        public Form1()
        {
            InitializeComponent();

            //해당 리소스가 해제되면 폰트 오류가 발생되므로 전역 변수로 선언 필요.
            privateFontCollection = new PrivateFontCollection();
            privateFontCollection.AddFontFile(Path.Combine(Application.StartupPath, "ChangwonDangamAsac-Bold_0712.ttf"));

            SetControlFont(this);
        }

        private void SetControlFont (Control parent)
        {
            foreach (Control child in parent.Controls) //모든 하위 컨트롤들에 대해서 적용시켜줍니다.
            {
                child.Font = new Font(privateFontCollection.Families[0], child.Font.Size, child.Font.Style);

                if (child.Controls.Count > 0) //panel, groupbox 와 같이 하위 컨트롤이 더 있는 경우도 적용시켜줍니다.
                {
                    SetControlFont(child);
                }
            }
        }
    }
}

1) 상하좌우 크기 조절 기능.

2) 최대화, 최소화, 종료 버튼

3) 타이틀 버튼 설정 (마우스 클릭, 더블 클릭)

 

TestForm (FormParent 상속 받아서 기능 사용.)

namespace FormStyleNoneResize
{
    public partial class TestForm : FormParent
    {
        public TestForm()
        {
            InitializeComponent();

            this.SetTitle(label1); //타이틀 (마우스 움직임 등 설정)
            this.SetCloseButton(button1); //닫기 이벤트 추가
        }
    }
}

 

FormParent Class

using System;
using System.Drawing;
using System.Windows.Forms;

namespace FormStyleNoneResize
{
    public partial class FormParent : Form
    {
        //https://docs.microsoft.com/ko-kr/windows/win32/inputdev/wm-nchittest
        private const int WM_NCHITTEST = 0x84; //마우스 커서 위치가 크기 조절 가능한 부분에 있는 경우
        private const int HTCLIENT = 1;
        private const int HTLEFT = 10;
        private const int HTRIGHT = 11;
        private const int HTTOP = 12;
        private const int HTTOPLEFT = 13;
        private const int HTTOPRIGHT = 14;
        private const int HTBOTTOM = 15;
        private const int HTBOTTOMLEFT = 16;
        private const int HTBOTTOMRIGHT = 17;

        private const int GRIP = 5; //리사이즈 잡는 영역 크기

        private Point prePosition;
        private Point mouseDownPoint;

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_NCHITTEST)
            {
                base.WndProc(ref m);

                if ((int)m.Result == HTCLIENT)
                {
                    Point position = this.PointToClient(Cursor.Position);

                    bool left = position.X <= GRIP;
                    bool right = this.Width - GRIP <= position.X;
                    bool top = position.Y <= GRIP;
                    bool bottom = this.Height - GRIP <= position.Y;

                    if (left && top) m.Result = (IntPtr)HTTOPLEFT;
                    else if (right && top) m.Result = (IntPtr)HTTOPRIGHT;
                    else if (left && bottom) m.Result = (IntPtr)HTBOTTOMLEFT;
                    else if (right && bottom) m.Result = (IntPtr)HTBOTTOMRIGHT;
                    else if (left) m.Result = (IntPtr)HTLEFT;
                    else if (right) m.Result = (IntPtr)HTRIGHT;
                    else if (top) m.Result = (IntPtr)HTTOP;
                    else if (bottom) m.Result = (IntPtr)HTBOTTOM;
                }

                return;
            }

            base.WndProc(ref m); //다른 처리를 위한 기존 콜백 다시 호출해주기
        }

        public FormParent()
        {
            InitializeComponent();
        }

        //마우스 이벤트가 발생할 타이틀바를 등록합니다.
        protected void SetTitle(Control titleControl)
        {
            titleControl.MouseDoubleClick += new MouseEventHandler(titleControl_MouseDoubleClick);
            titleControl.MouseDown += new MouseEventHandler(titleControl_MouseDown);
            titleControl.MouseMove += new MouseEventHandler(titleControl_MouseMove);
        }

        //최소화 버튼을 등록합니다.
        protected void SetMinimumButton(Button button)
        {
            button.Click += new EventHandler(this.button_minimum_Click);
        }

        //최대화 버튼을 등록합니다.
        protected void SetMaximumButton(Button button)
        {
            button.Click += new EventHandler(this.button_maximum_Click);
        }

        protected void SetCloseButton(Button button)
        {
            button.Click += new EventHandler(this.button_close_Click);
        }

        private void titleControl_MouseDoubleClick(object sender, MouseEventArgs e) //상단 타이틀 더블 클릭
        {
            if (e.Button != MouseButtons.Left) //왼쪽 버튼만 사용
            {
                return;
            }

            if (WindowState != FormWindowState.Maximized)
            {
                SetMaximize();
            }
            else
            {
                SetNormal();
            }
        }

        private void titleControl_MouseDown(object sender, MouseEventArgs e) //상단 타이틀 클릭 시 움직일 준비
        {
            if (e.Button != MouseButtons.Left) //왼쪽 버튼만 사용
            {
                return;
            }

            mouseDownPoint = new Point(e.X, e.Y);
        }

        private void titleControl_MouseMove(object sender, MouseEventArgs e) //상단 타이틀을 통한 이동
        {
            if ((e.Button & MouseButtons.Left) == MouseButtons.Left) //왼쪽 버튼이 눌린 경우만 움직여줍니다.
            {
                this.Left += e.X - mouseDownPoint.X;
                this.Top += e.Y - mouseDownPoint.Y;
            }
        }

        private void button_minimum_Click(object sender, EventArgs e) //최소화 버튼
        {
            WindowState = FormWindowState.Minimized;
        }

        private void button_maximum_Click(object sender, EventArgs e) //최대화 버튼
        {
            if (WindowState != FormWindowState.Maximized)
            {
                SetMaximize();
            }
            else
            {
                SetNormal();
            }
        }

        private void SetNormal() //기존 사이즈로 변경
        {
            this.Location = prePosition; //크기 변경 시 원 위치로 복귀
            this.WindowState = FormWindowState.Normal;
        }

        private void SetMaximize() //최대화
        {
            Screen screen = Screen.FromControl(this); //현재 Form과 가장 가까운 Screen 찾기.
            prePosition = this.Location; //노멀화 복귀 좌표값

            this.Location = screen.Bounds.Location; //위치 지정
            this.MaximizedBounds = new Rectangle(0, 0, screen.WorkingArea.Width, screen.WorkingArea.Height); //최대화 크기 변경
            this.WindowState = FormWindowState.Maximized;
        }

        //종료 버튼
        private void button_close_Click(object sender, EventArgs e)
        {
            Close();
        }
    }
}

 

아래와 같이 이미지 및 문자 색상을 나타낼 수 있는 버튼을 만드는 코드입니다.

마우스 이동 시 이벤트가 자동으로 발생되어 지정한 이미지가 나타나도록 가능합니다. (텍스트 등 필요한 기능은 직접 추가!)


 

프로젝트에 Button을 상속받은 아래의 클래스를 생성 후 "프로젝트 빌드" 를 해주어야 사용 가능해집니다.

그리고 Image_Disable, Image_Disable_Color ... 에 대해 이미지 및 텍스트 색상 지정을 해주면 끝!

이미지 등록하는 모습

 

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public class MyButton : Button
    {
        public Image Image_Normal //초기 이미지
        {
            get
            {
                return image_Normal;
            }
            set
            {
                image_Normal = value;
                this.BackgroundImage = value; //디자이너에서 초기 이미지 변경 시 수정되도록 추가.
            }
        }
        private Image image_Normal;

        public Image Image_Hover { get; set; } //마우스 포커싱 이미지
        public Image Image_Press { get; set; } //마우스 누를시 이미지
        public Image Image_Disable { get; set; } //비활성화 이미지
        public Color Image_Normal_Color { get; set; } = Color.Black; //이미지 초기 글자색
        public Color Image_Hover_Color { get; set; } = Color.Black; //이미지 포커싱 글자색
        public Color Image_Press_Color { get; set; } = Color.White; //마우스 누를 시 글자색
        public Color Image_Disable_Color { get; set; } = Color.Black; //비활성화 글자색

        public new bool Enabled //활성화/비활성화 여부에 따라서 이미지 및 글자색 지정
        {
            get
            {
                return base.Enabled;
            }
            set
            {
                base.Enabled = value;

                if (value)
                {
                    this.BackgroundImage = Image_Normal;
                    this.ForeColor = Image_Normal_Color;
                }
                else
                {
                    this.BackgroundImage = Image_Disable;
                    this.ForeColor = Image_Disable_Color;
                }
            }
        }

        private bool isMouseDown;

        public MyButton()
        {
            this.FlatStyle = FlatStyle.Flat;
            this.FlatAppearance.BorderSize = 0;
            this.FlatAppearance.MouseOverBackColor = Color.Transparent; //배경 투명
            this.FlatAppearance.MouseDownBackColor = Color.Transparent; //배경 투명
            this.FlatAppearance.CheckedBackColor = Color.Transparent; //배경 투명
            this.BackColor = Color.Transparent; //배경 투명

            this.BackgroundImageLayout = ImageLayout.Zoom; //이미지 보여주는 형태

            this.MouseMove += MyButton_MouseMove;
            this.MouseDown += MyButton_MouseDown;
            this.MouseLeave += MyButton_MouseLeave;
            this.MouseUp += MyButton_MouseUp;
        }

        private void MyButton_MouseMove(object sender, EventArgs e)
        {
            if (isMouseDown)
            {
                return;
            }

            SetBackgroundImage(Image_Hover);
            SetForegroundColor(Image_Hover_Color);
        }

        private void MyButton_MouseLeave(object sender, EventArgs e)
        {
            SetBackgroundImage(Image_Normal);
            SetForegroundColor(Image_Normal_Color);
        }

        private void MyButton_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) //왼쪽 버튼이 아니면 무시
            {
                return;
            }

            isMouseDown = true;
            SetBackgroundImage(Image_Press);
            SetForegroundColor(Image_Press_Color);
        }

        private void MyButton_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) //왼쪽 버튼이 아니면 무시
            {
                return;
            }

            isMouseDown = false;
            SetBackgroundImage(Image_Normal);
            SetForegroundColor(Image_Normal_Color);
        }

        private void SetBackgroundImage(Image image)
        {
            if (this.Enabled == false)
            {
                return;
            }

            this.BackgroundImage = image;
        }

        private void SetForegroundColor(Color color)
        {
            if (this.Enabled == false)
            {
                return;
            }

            this.ForeColor = color;
        }
    }
}

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);
        }
    }
}

C# 에서 기본적으로 제공되는 압축 해제 기능으로

System.IO.Compression 가 있습니다.

암호화는 지원하지 않지만 기본 내장되어 있고 사용도 편리합니다.

 

ZipFile 클래스 사용을 위해선 System.IO.Compression.FileSystem 참조가 필요합니다.

 

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression; //ZipFile 에 필요
using System.Windows.Forms;

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

        private void button1_Click(object sender, EventArgs e) //zip 생성
        {
            string workFolder = Application.StartupPath;
            string targetDirectory = Path.Combine(workFolder, "TestFiles");
            string savePath = Path.Combine(workFolder, "Zipfile.zip");

            if (File.Exists(savePath)) //기존 파일은 제거
            {
                File.Delete(savePath);
            }

            ZipFile.CreateFromDirectory(targetDirectory, savePath);
            Process.Start(workFolder); //폴더 열어서 확인
        }

        private void button2_Click(object sender, EventArgs e) //zip 해제
        {
            string workFolder = Application.StartupPath;
            string targetPath = Path.Combine(workFolder, "Zipfile.zip");
            string saveFolder = Path.Combine(workFolder, "unZip");

            if (Directory.Exists(saveFolder)) //기존 파일은 제거
            {
                Directory.Delete(saveFolder, true);
            }
            
            ZipFile.ExtractToDirectory(targetPath, saveFolder);
            Process.Start(workFolder); //폴더 열어서 확인
        }
    }
}

 

 

파일 압축 및 풀기 결과

 

그 밖에도 다른 라이브러리들이 있는데, NuGet 에서 받아서 사용이 가능합니다.

GPT에서 제공한 표를 남겨두겠습니다.

 

- GPT 정리 내용

라이브 러리 암호화 포맷 속도 최신 유지장점 요약 (2025년 10월 06일 기준!)

라이브러리 암호화 포맷 속도 최신 유지 장점 요약
System.IO.Compression ZIP ✅ 빠름 ✅ 최신 .NET 내장, 간단
DotNetZip (Ionic.Zip) ZIP ⚪ 보통 ❌ 구버전 암호 ZIP 지원
SharpZipLib ZIP, TAR, GZIP 등 ⚪ 보통 ✅ 유지보수 중 다양한 포맷
SharpCompress ⚠️ 제한적 ZIP, 7z, TAR 등 ⚪ 보통 ✅ 최신 스트리밍 지원
SevenZipSharp ✅ AES 7z ⚠️ 느림 ⚪ 부분적 고압축, 보안

 

DotNetZip 간단 사용법 예시

using Ionic.Zip;

// 압축
using (var zip = new ZipFile())
{
    zip.Password = "1234"; // 암호 설정
    zip.AddDirectory(@"C:\TestSource");
    zip.Save(@"C:\Result.zip");
}

// 해제
using (var zip = ZipFile.Read(@"C:\Result.zip"))
{
    zip.Password = "1234";
    zip.ExtractAll(@"C:\Extracted", ExtractExistingFileAction.OverwriteSilently);
}

DateTime 으로 시간을 확인 할 수 있지만, Stopwatch 를 통해 경과 시간을 확인 할 수 있습니다.

내부 요소를 통해 아래의 방법대로 표현도 가능합니다.

 

 

using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch stopWatch = Stopwatch.StartNew(); //객체 만들자마자 Start()까지 실행됨
            
            while (stopWatch.ElapsedMilliseconds < 5000) //5초
            {
                try
                {
                    using (TcpClient tcpClient = new TcpClient()) //연결만 확인.
                    {
                        Task task = tcpClient.ConnectAsync("127.0.0.1", 8080); //연결할 주소
                        if (task.Wait(3000) && tcpClient.Connected) //연결 확인
                        {
                            tcpClient.Close();
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    //Console.WriteLine(ex); //서버 없으면 오류나서 주석처리
                }

                Console.WriteLine($"tick: {stopWatch.ElapsedTicks}");
                Console.WriteLine($"milliseconds: {stopWatch.ElapsedMilliseconds}");

                TimeSpan timeSpan = stopWatch.Elapsed;
                Console.WriteLine($"기본: {timeSpan}");
                Console.WriteLine($"초: {timeSpan.TotalSeconds}"); //전체 초 단위
                Console.WriteLine($"ms: {timeSpan.TotalMilliseconds}"); //전체 밀리초 단위
                Console.WriteLine($"포맷: {timeSpan:hh\\:mm\\:ss\\.fff}"); //00:00:00.000

                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }
}

오랜만에 기본기에 대해 생각을 해보게 되었습니다.

값 복사 형식과 참조 형식.. (잊고 있던 포인터..)

코드에 대부분 클래스를 사용하다보니 잊고 있었는데 복습할 겸 글을 남겨봅니다.

가독성을 위해 foreach 를 사용하려고 하는데 구조체에 대해서는 메모리 낭비가 발생 되고,

foreach 내부에서 원본의 값을 수정할 수 없다는 단점이 있더군요..!

추가로 배열 사이즈 변경도 남겨봅니다.

 

 

using System;
using System.Collections.Generic;

namespace ConsoleApp2
{
    class Program
    {
        struct ValueType
        {
            public int a;
            public int b;
        }

        class ReferenceType
        {
            public int a;
            public int b;
        }

        static void Main(string[] args)
        {
            ValueType valueType; //new ValueType() 없이 사용 가능.
            valueType.a = 1;
            valueType.b = 2;

            ValueType valueType2 = valueType; //값 복사됨.
            valueType2.a = 3;
            valueType2.b = 4;

            ReferenceType referenceType = new ReferenceType(); //반드시 메모리 할당 필요
            referenceType.a = 1;
            referenceType.b = 2;

            ReferenceType referenceType2 = referenceType; //같은 메모리 바라봄
            referenceType2.a = 3;
            referenceType2.b = 4;

            Console.WriteLine("valueType : {0} {1}", valueType.a, valueType.b);
            Console.WriteLine("valueType2 : {0} {1}", valueType2.a, valueType2.b);

            Console.WriteLine("referenceType : {0} {1}", referenceType.a, referenceType.b);
            Console.WriteLine("referenceType2 : {0} {1}", referenceType2.a, referenceType2.b);

            List<ValueType> valueTypeList = new List<ValueType>();
            List<ReferenceType> referenceTypeList = new List<ReferenceType>();

            //ValueType은 foreach에서 값 변경이 불가능하다, 또한 메모리 낭비가 발생 될 수 있다.
            foreach (ValueType sub in valueTypeList)
            {
                Console.WriteLine("{0} {1}", sub.a, sub.b);
                //sub.a = 40; //구조체는 오류 발생. 복사 값이므로 원본을 바꿀 수 없어 컴파일 오류.
            }

            foreach (ReferenceType sub in referenceTypeList) //foreach 도 참조형태 - 원본 값 바뀜
            {
                Console.WriteLine("{0} {1}", sub.a, sub.b);
                sub.a = 40; //원본 값 변경 됨.
            }

            ValueType[] valueTypeArray = null;
            Array.Resize(ref valueTypeArray, 5); //new ValueType[5]

            for (int i = 0; i < valueTypeArray.Length; ++i)
            {
                Console.WriteLine("배열 개수 : {0}", i + 1);
            }

            Console.ReadLine();
        }
    }
}

A프로그램에서 B프로그램에게 요청하여 동작을 할 일이 있었습니다.

B프로그램은 초기화 되며 열려있는 창들이 닫혀야 했는데 이 부분에 대한 구현이 필요했습니다.

 

ex)

Program (B) 기능 -> OpenFileDialog (윈도우 API) Custom Form (사용자Form)

 

Program (B) 가 ShowDialog 로 잡혀있는 상황입니다.

윈도우API 종료는 SendMessage 를 사용해야 하고, Form은 Close를 통해 종료해 줄 수 있습니다.

 

(NamedPipe를 사용하였고, BlockingCollection Queue 를 사용하여 순서유지를 보장하였습니다.

코드가 좀 길어졌지만.. 남겨봅니다!

 

ShowDialog() 종료하는 프로그램

 

A프로그램 (명령 내리는 코드)

using System;
using System.IO;
using System.IO.Pipes;
using System.Windows.Forms;

namespace WindowsFormsApp6
{
    public partial class Form2 : Form
    {
        private const string targetName = "A_Pipe";

        public Form2()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            SendNamedPipe("CloseAPI");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            SendNamedPipe("CloseSubForm");
        }

        private void SendNamedPipe(string message)
        {
            try
            {
                NamedPipeClientStream namedPipeClientStream = new NamedPipeClientStream(".", targetName, PipeDirection.InOut);
                namedPipeClientStream.Connect(10000); //10초

                StreamWriter streamWriter = new StreamWriter(namedPipeClientStream);

                streamWriter.WriteLine(message);
                streamWriter.Flush();

                streamWriter.Close();
                namedPipeClientStream.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}

 

 

B프로그램 (ShowDialog 하는 프로그램)

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form3 : Form
    {
        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        private const uint WM_CLOSE = 0x0010;
        private const string myName = "A_Pipe"; //이름만 바꿔서 사용 가능

        private BlockingCollection<string> queue = new BlockingCollection<string>();
        private int queueCount = 0;

        private void ProcessQueue()
        {
            //내부적으로 monitor lock 걸려있어 순서 및 단일 실행 보장
            foreach (string message in queue.GetConsumingEnumerable())
            {
                if (message == "CloseAPI")
                {
                    CloseAPI();
                }
                else if (message == "CloseSubForm")
                {
                    CloseSubForm();
                }

                Invoke((MethodInvoker)delegate
                {
                    queueCount++;
                    listBox1.Items.Add(string.Format("{0}_{1}", queueCount, message));
                });
            }
        }

        public Form3()
        {
            InitializeComponent();

            //큐에서 관리
            Task.Run(() =>
            {
                ProcessQueue();
            });

            //네임드파이프 통신
            Task.Run(() =>
            {
                Receive();
            });
        }

        private void Receive()
        {
            while (true)
            {
                try
                {
                    NamedPipeServerStream namedPipeServerStream = new NamedPipeServerStream(myName, PipeDirection.InOut, 10);
                    namedPipeServerStream.WaitForConnection(); //데이터 받을때까지 기다립니다.

                    StreamReader streamReader = new StreamReader(namedPipeServerStream);
                    string message = streamReader.ReadLine();

                    queue.Add(message); //큐에 데이터 쌓아주기

                    streamReader.Close();
                    namedPipeServerStream.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "불러오기 테스트";

            //테스트하기 위해 띄워주기만 함.
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                //정상 OK 눌렀을 때 실행
            }
            else
            {
                //강제 종료시 이 곳을 실행.
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //테스트용으로 단순 띄워주기만 진행
            new Form1().ShowDialog();
        }

        private void CloseAPI()
        {
            IntPtr hWnd = FindWindow(null, "불러오기 테스트"); //타이틀명으로 찾기
            if (hWnd != IntPtr.Zero)
            {
                SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); //종료 메시지 보내기
            }
        }

        private void CloseSubForm()
        {
            List<Form> formList = new List<Form>();

            foreach (Form form in Application.OpenForms)
            {
                // 메인폼 제외
                if (form is Form3 == false)
                {
                    formList.Add(form);
                }
            }

            foreach (Form form in formList)
            {
                Invoke((MethodInvoker)delegate
                {
                    form.Close();
                });
            }
        }
    }
}

파일 등을 드래그 & 드롭으로 가져올 수 있는 코드입니다.

dropFiles 는 해당 파일의 경로를 불러오게 됩니다.

 

파일/폴더를 마우스 드래그로 가져올 수 있습니다.

 

using System.Windows.Forms;

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

            listBox1.AllowDrop = true; //drag & drop 허용
        }

        private void listBox1_DragDrop(object sender, DragEventArgs e)
        {
            //드래그 드롭 파일 (여러 개 가능)
            string[] dropFiles = (string[])e.Data.GetData(DataFormats.FileDrop);
            
            foreach (string file in dropFiles)
            {
                listBox1.Items.Add(file);
            }
        }

        private void listBox1_DragEnter(object sender, DragEventArgs e)
        {
            //마우스 커서 표현
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
        }
    }
}

+ Recent posts