1) NuGet 패키지 관리자 -> 패키지 관리자 콘솔 ->

  Install-Package System.Data.SQLite    (입력)

  ※ Install-Package System.Data.SQLite.Core (정상 실행이 되지 않으면 Core로 설치)

 

Create, Insert, Update, Delete, Select 결과

 

아래는 예제 코드입니다.

Connection을 유지해도 괜찮지만 가벼운 SQLite 특성상 매번 접근해줘도 성능에 무리는 없다고 하네요.

 

connectString에서

Data Source=mydb.db

선언을 통해 mydb.db 파일에 접근이 가능해집니다. (파일이 없다면 자동 생성합니다.)

using System.Windows.Forms;
using System.Data.SQLite;
using System.Collections.Generic;
using System;

namespace WindowsFormsApp_SQLite
{
    public partial class Form1 : Form
    {
        class Data
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public int Age { get; set; }
        }

        private const string connectString = "Data Source=mydb.db;Version=3;";

        public Form1()
        {
            InitializeComponent();
        }

        private void CreateTable()
        {
            using (SQLiteConnection connection = new SQLiteConnection(connectString))
            {
                connection.Open();
                string query = @"CREATE TABLE IF NOT EXISTS Users 
                                      (Id INTEGER PRIMARY KEY AUTOINCREMENT,
                                       Name TEXT NOT NULL,
                                       Age INTEGER)";

                using (SQLiteCommand command = new SQLiteCommand(query, connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }

        private void Insert(string name, int age)
        {
            using (SQLiteConnection connection = new SQLiteConnection(connectString))
            {
                connection.Open();
                string query = "INSERT INTO Users (Name, Age) VALUES (@name, @age)";

                using (SQLiteCommand command = new SQLiteCommand(query, connection))
                {
                    command.Parameters.AddWithValue("@name", name);
                    command.Parameters.AddWithValue("@age", age);
                    command.ExecuteNonQuery();
                }
            }
        }

        private List<Data> SelectAll()
        {
            List<Data> dataList = new List<Data>();

            using (SQLiteConnection connection = new SQLiteConnection(connectString))
            {
                connection.Open();
                string query = "SELECT * FROM Users";

                using (SQLiteCommand command = new SQLiteCommand(query, connection))
                {
                    using (SQLiteDataReader reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            dataList.Add(new Data
                            {
                                Id = Convert.ToInt32(reader["Id"]),
                                Name = reader["Name"].ToString(),
                                Age = Convert.ToInt32(reader["Age"])
                            });
                        }
                    }
                }
            }

            return dataList;
        }

        private void Update(int id, string name, int age)
        {
            using (SQLiteConnection connection = new SQLiteConnection(connectString))
            {
                connection.Open();
                string query = "UPDATE Users SET Name=@name, Age=@age WHERE Id=@id";

                using (SQLiteCommand command = new SQLiteCommand(query, connection))
                {
                    command.Parameters.AddWithValue("@id", id);
                    command.Parameters.AddWithValue("@name", name);
                    command.Parameters.AddWithValue("@age", age);
                    command.ExecuteNonQuery();
                }
            }
        }

        private void Delete(int id)
        {
            using (SQLiteConnection connection = new SQLiteConnection(connectString))
            {
                connection.Open();
                string query = "DELETE FROM Users WHERE Id=@id";

                using (SQLiteCommand command = new SQLiteCommand(query, connection))
                {
                    command.Parameters.AddWithValue("@id", id);
                    command.ExecuteNonQuery();
                }
            }
        }

        private void button1_Click(object sender, System.EventArgs e)
        {
            CreateTable();
            Insert("테스트1", 30);
            Insert("테스트2", 20);

            textBox1.Text = string.Format("Insert 후 시작{0}", Environment.NewLine);

            List<Data> dataList = SelectAll();
            PrintData(dataList);

            Update(dataList[0].Id, "업데이트1", 70);
            Delete(dataList[1].Id);
            textBox1.Text += string.Format("업데이트 & 삭제{0}", Environment.NewLine);

            dataList = SelectAll();
            PrintData(dataList);

            //전체 제거
            for (int i = 0; i < dataList.Count; ++i)
            {
                Delete(dataList[i].Id);
            }
        }

        private void PrintData(List<Data> dataList)
        {
            foreach (Data data in dataList)
            {
                textBox1.Text += string.Format("Id:{0} Name:{1} Age:{2}{3}", data.Id, data.Name, data.Age, Environment.NewLine);
            }
        }
    }
}

커스텀 트리뷰어

 

Tag 값을 옆쪽에 출력하고 깜박임 없는 트리에 대한 코드입니다.

SendMessage를 이용하여 트리뷰 더블버퍼를 켜줘야 반짝임이 없어집니다.

 

아래는 트리뷰어 코드입니다.

[CustomTreeView]

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

namespace WindowsFormsApp_Tree
{
    class CustomTreeView : TreeView
    {
        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

        private const int tagOffset = 5; //태그 간격
        private readonly Size dragSize = SystemInformation.DragSize;

        private Font tagFont = new Font(FontFamily.GenericSerif, 10); //폰트 및 사이즈
        private const int TVM_SETEXTENDEDSTYLE = 0x1100 + 44; //TreeViewMessage
        private const int TVS_EX_DOUBLEBUFFER = 0x0004; //TreeViewStyle Extended

        private Form parentForm = null;
        private bool cancelPainting = false; //종료 시 깜빡임 방지

        private TreeNode dragNode = null;
        private bool isDragging = false;
        private Point dragStartPoint;

        public CustomTreeView()
        {
            this.ItemHeight = 18; //트리 아이템 높이
            this.DrawMode = TreeViewDrawMode.OwnerDrawText; //수동 그리기
            this.Scrollable = true;
            this.AllowDrop = true; //Drag & Drop
        }

        //프로그램 종료 시 트리구조 깨지는 증상 방지용
        protected override void OnParentChanged(EventArgs e)
        {
            base.OnParentChanged(e);

            if (parentForm != null)
            {
                parentForm.FormClosed -= OnFormClosing;
            }

            parentForm = FindForm();

            if (parentForm != null) //Remove 시에는 null 반환되므로 방어코드
            {
                parentForm.FormClosed += OnFormClosing;
            }
        }

        //프로그램 종료 시 트리구조 깨지는 증상 방지용
        private void OnFormClosing(object sender, FormClosedEventArgs e)
        {
            cancelPainting = true;
        }

        //네이티브 더블 버퍼링 (가장 효과적)
        protected override void OnHandleCreated(EventArgs e)
        {
            SendMessage(Handle, TVM_SETEXTENDEDSTYLE, (IntPtr)TVS_EX_DOUBLEBUFFER, (IntPtr)TVS_EX_DOUBLEBUFFER);
            base.OnHandleCreated(e);
        }

        //직접 그려주기
        protected override void OnDrawNode(DrawTreeNodeEventArgs e)
        {
            if (cancelPainting) //종료 시 깜빡임 제거
            {
                return;
            }

            if (e.Node.IsVisible == false) //보이지 않는 노드는 무시
            {
                return;
            }

            e.DrawDefault = false;

            //선택 여부에 따른 배경/텍스트 색상
            bool selected = (e.State & TreeNodeStates.Selected) != 0;
            Color bgColor = selected ? SystemColors.Highlight : this.BackColor;
            Color textColor = selected ? SystemColors.HighlightText : Color.Black;

            //배경 지우기
            using (SolidBrush bgBrush = new SolidBrush(bgColor))
            {
                e.Graphics.FillRectangle(bgBrush, e.Bounds);
            }

            //노드 텍스트 정렬 그리기
            TextRenderer.DrawText(e.Graphics,
                                  e.Node.Text,
                                  e.Node.TreeView.Font,
                                  e.Bounds,
                                  textColor,
                                  TextFormatFlags.VerticalCenter | TextFormatFlags.Left);

            if (e.Node.Tag != null) //태그 내용 그려주기
            {
                float y = e.Bounds.Top + (e.Bounds.Height - tagFont.Height) / 2f;
                e.Graphics.DrawString(e.Node.Tag.ToString(), tagFont, Brushes.Red, e.Bounds.Right + tagOffset, y);
            }
        }

        //글자 외 영역에 대한 더블 클릭 (펼치기/닫기)
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            TreeNode node = this.GetNodeAt(e.X, e.Y);
            if (node != null && node.Nodes.Count > 0)
            {
                node.Toggle();
            }

            base.OnMouseDoubleClick(e);
        }

        //****************************************
        //텍스트 영역에서만 드래그 가능
        ////드래그
        //protected override void OnItemDrag(ItemDragEventArgs e)
        //{
        //    dragNode = (TreeNode)e.Item;
        //    DoDragDrop(e.Item, DragDropEffects.Move);
        //}

        //*****************************************

        //드래그 허용
        protected override void OnDragEnter(DragEventArgs e)
        {
            e.Effect = DragDropEffects.Move;
        }

        //모든 영역에서 드래그 시에도 이동 가능
        protected override void OnMouseDown(MouseEventArgs e)
        {
            TreeNode node = this.GetNodeAt(e.X, e.Y);
            if (node != null)
            {
                this.SelectedNode = node;
                dragNode = node;
                dragStartPoint = e.Location;
                isDragging = false;
            }
            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && dragNode != null && !isDragging)
            {
                //일정 거리 이상 움직여야 드래그 (클릭 오작동 방지)
                if (Math.Abs(e.X - dragStartPoint.X) > dragSize.Width || 
                    Math.Abs(e.Y - dragStartPoint.Y) > dragSize.Height)
                {
                    isDragging = true;
                    DoDragDrop(dragNode, DragDropEffects.Move);
                }
            }
            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            dragNode = null;
            isDragging = false;
            base.OnMouseUp(e);
        }

        private bool IsChildNode(TreeNode parent, TreeNode child)
        {
            TreeNode node = child.Parent;

            while (node != null) //부모 노드들을 순회
            {
                if (node == parent)
                {
                    return true;
                }

                node = node.Parent;
            }

            return false;
        }

        protected override void OnDragOver(DragEventArgs e)
        {
            Point point = this.PointToClient(new Point(e.X, e.Y));
            TreeNode targetNode = this.GetNodeAt(point);

            if (targetNode != null)
            {
                this.SelectedNode = targetNode;
            }

            if (dragNode != null && dragNode.Level == 0 && targetNode != null && targetNode.Level != 0)
            {
                e.Effect = DragDropEffects.None;
            }
            else
            {
                e.Effect = DragDropEffects.Move;
            }
        }

        //필요 시 로직 수정. 레벨0 끼리는 제한. 레벨1부터는 자유롭게 하위로 등록 가능.
        protected override void OnDragDrop(DragEventArgs e)
        {
            Point point = this.PointToClient(new Point(e.X, e.Y));
            TreeNode targetNode = this.GetNodeAt(point);

            if (dragNode == null)
            {
                return;
            }

            if (targetNode == null)
            {
                if (dragNode.Level == 0)
                {
                    dragNode.Remove();
                    this.Nodes.Insert(this.Nodes.Count, dragNode);
                    this.SelectedNode = dragNode;
                    return;
                }

                return;
            }

            //동일한 노드, 자식 노드를 현재 노드에 옮기는 것은 방지
            if (targetNode == dragNode || IsChildNode(dragNode, targetNode))
            {
                return;
            }

            if (dragNode.Level == 0)
            {
                int targetIndex = targetNode.Level == 0 ? targetNode.Index : targetNode.Parent.Index;
                dragNode.Remove();
                this.Nodes.Insert(targetIndex, dragNode);
                this.SelectedNode = dragNode;
                return;
            }

            dragNode.Remove();
            targetNode.Nodes.Add(dragNode);
            targetNode.Expand();
            this.SelectedNode = dragNode;
        }
    }
}

 

 

트리뷰어 사용 코드입니다.

[MainForm]

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

namespace WindowsFormsApp_Tree
{
    public partial class Form1 : Form
    {
        private int nodeSequence;
        
        public Form1()
        {
            InitializeComponent();
        }

        private void form1_Load(object sender, EventArgs e)
        {
            //customTreeView1.ShowLines = false; //왼쪽 점선 라인
            customTreeView1.CheckBoxes = true; //체크박스 추가
            customTreeView1.DrawMode = TreeViewDrawMode.OwnerDrawText;

            string folderNormal = @"C:\TestImage\tree_folder_normal.png";
            string folderSelect = @"C:\TestImage\tree_folder_select.png";
            string fileNormal = @"C:\TestImage\tree_file_normal.png";
            string fileSelect = @"C:\TestImage\tree_file_select.png";

            ImageList imageList = new ImageList();
            imageList.Images.Add(Image.FromFile(folderNormal));
            imageList.Images.Add(Image.FromFile(folderSelect));
            imageList.Images.Add(Image.FromFile(fileNormal));
            imageList.Images.Add(Image.FromFile(fileSelect));

            customTreeView1.ImageList = imageList; //이미지 인덱스
        }

        private TreeNode GetFolderNode()
        {
            nodeSequence++;

            TreeNode newNode = new TreeNode(nodeSequence.ToString("D4"), 0, 1); //폴더 이미지
            newNode.Tag = "Tag : " + nodeSequence.ToString("D4");
            newNode.Checked = true;
            return newNode;
        }

        private TreeNode GetFileNode()
        {
            nodeSequence++;

            TreeNode newNode = new TreeNode(nodeSequence.ToString("D4"), 2, 3); //파일 이미지
            newNode.Tag = "Tag : " + nodeSequence.ToString("D4");
            newNode.Checked = true;
            return newNode;
        }

        private void button_AddFolder_Click(object sender, EventArgs e)
        {
            TreeNode newNode = GetFolderNode();

            customTreeView1.Nodes.Add(newNode);
            customTreeView1.SelectedNode = newNode;
        }

        private void button_AddFile_Click(object sender, EventArgs e)
        {
            if (customTreeView1.SelectedNode == null)
            {
                return;
            }

            TreeNode newNode = GetFileNode();
            customTreeView1.SelectedNode.Nodes.Add(newNode);
            newNode.EnsureVisible(); //노드 보여주기.
        }
        
        private void button_AddFile100_Click(object sender, EventArgs e)
        {
            TreeNode selectedNode = customTreeView1.SelectedNode;
            
            if (selectedNode == null)
            {
                if (customTreeView1.Nodes.Count > 0)
                {
                    selectedNode = customTreeView1.Nodes[0];
                }
                else
                {
                    selectedNode = GetFolderNode();
                    customTreeView1.Nodes.Add(selectedNode);
                }
            }

            for (int i = 0; i < 100; ++i)
            {
                selectedNode.Nodes.Add(GetFileNode());
            }

            customTreeView1.ExpandAll();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            TreeNode selectedNode = customTreeView1.SelectedNode;

            if (selectedNode != null)
            {
                selectedNode.Remove();
            }
        }
    }
}

button1은 CustomPanel 하위 목록으로 들어가면서 추가 될 필드를 지정할 수 있습니다.

TableLayout 에서 Row, RowSpan 등의 속성이 추가되는 것이 궁금하여 찾아본 내용 정리입니다.

CustomPanel 하위 목록에 대해서 디자이너에서 보여줄 필드를 생성 가능

 

Panel 외에도 다른 컨트롤을 상속받아서도 가능합니다.

내부적으로 Dictionary 처리를 통해 값을 별도로 관리할 수 있습니다.

ProvideProperty 는 여러 개를 사용할 수 있습니다.

IExtenderProvider 를 상속받아야 하며 Setter와 Getter를 구현해주어야 사용 가능합니다.

아래는 예시 코드입니다.

using System.Collections.Generic;
using System.ComponentModel; //IExtenderProvider
using System.Windows.Forms;

namespace WindowsFormsApp6
{
    //해당 이름 사용. Get, Set 구현 필수
    //CustomField 이기때문에 GetCustomField(Control, 타입), SetCustomField(Control) 메소드 필요.
    [ProvideProperty("CustomField", typeof(Control))]
    class CustomPanel : Panel, IExtenderProvider
    {
        private readonly Dictionary<Control, int> fieldDict = new Dictionary<Control, int>();

        public bool CanExtend(object extendee)
        {
            Control control = extendee as Control;

            if (control == null)
            {
                return false;
            }

            //조건에 따라 전체 컴포넌트에 붙을 수 있음.
            return control.Parent == this; //자식 컴포넌트에만 붙여주기
        }

        [DisplayName("CustomField")] //없으면 클래스명의 필드로 이름 지정됨.
        public int GetCustomField(Control control)
        {
            if (fieldDict.TryGetValue(control, out int value))
            {
                return value;
            }

            return 0; //default value
        }

        public void SetCustomField(Control control, int value)
        {
            fieldDict[control] = value;
        }
    }
}

 

 

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; //비활성화 글자색
        
        private bool isMouseDown;

        public CustomButton()
        {
            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; //이미지 보여주는 형태

            TabStop = false; //키보드 이동하여 테두리 그리기 방지
            SetStyle(ControlStyles.Selectable, false); //선택 테두리 방지
        }

        private void SetNormal()
        {
            this.BackgroundImage = Image_Normal;
            this.ForeColor = Image_Normal_Color;
        }

        private void SetHover()
        {
            this.BackgroundImage = Image_Hover;
            this.ForeColor = Image_Hover_Color;
        }

        private void SetPress()
        {
            this.BackgroundImage = Image_Press;
            this.ForeColor = Image_Press_Color;
        }

        private void SetDisable()
        {
            this.BackgroundImage = Image_Disable;
            this.ForeColor = Image_Disable_Color;
        }

        protected override void OnEnabledChanged(EventArgs e) //Enable에 따라 변경.
        {
            base.OnEnabledChanged(e);
            isMouseDown = false;

            if (!Enabled)
            {
                SetDisable();
                return;
            }

            SetHoverOrNormal(this.PointToClient(Cursor.Position)); //활성화 상태 복구
        }

        private void SetHoverOrNormal(Point point) //비활성화 혹은 포커싱 잃은 후 복구
        {
            if (ClientRectangle.Contains(point)) //Enable 시 마우스 위치 확인
            {
                SetHover();
            }
            else
            {
                SetNormal();
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (isMouseDown)
            {
                return;
            }

            SetHover();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            SetNormal();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (e.Button != MouseButtons.Left) //왼쪽 버튼이 아니면 무시
            {
                return;
            }

            isMouseDown = true;
            SetPress();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            if (e.Button != MouseButtons.Left) //왼쪽 버튼이 아니면 무시
            {
                return;
            }

            isMouseDown = false;
            SetHoverOrNormal(e.Location); //마우스 위치에 따라 상태 변경
        }
    }
}

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.Windows.Forms;

namespace WindowsFormsApp1 //네임스페이스 맞춰줄 것.
{
    public class ThumbnailView : ListView
    {
        private int thumbnailWidth = 128;
        private int thumbnailHeight = 128;

        public ThumbnailView()
        {
            this.View = View.LargeIcon;
            this.MultiSelect = true; //여러 장 선택 여부
            this.OwnerDraw = false; //기본 그리기 사용 여부
            this.HideSelection = false; //포커싱 잃으면 선택 취소
            this.DoubleBuffered = true; //깜빡임 방지 더블버퍼

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

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

+ Recent posts