<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>저그유저의 프로그래밍</title>
    <link>https://zergling.tistory.com/</link>
    <description>정리</description>
    <language>ko</language>
    <pubDate>Sat, 27 Jun 2026 08:43:09 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>스타크래프트 좋아하는 사람</managingEditor>
    <item>
      <title>[C# Windows Form] SQLite 예제 (NuGet 설치 후 사용)</title>
      <link>https://zergling.tistory.com/125</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1) NuGet 패키지 관리자 -&amp;gt; 패키지 관리자 콘솔 -&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Install-Package System.Data.SQLite&amp;nbsp; &amp;nbsp; (입력)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;b&gt;※ Install-Package System.Data.SQLite.Core (정상 실행이 되지 않으면 Core로 설치)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;install.png&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;898&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mYV9N/dJMcaiQw8oT/y3T36MIInFyMPNSG0u0iiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mYV9N/dJMcaiQw8oT/y3T36MIInFyMPNSG0u0iiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mYV9N/dJMcaiQw8oT/y3T36MIInFyMPNSG0u0iiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmYV9N%2FdJMcaiQw8oT%2Fy3T36MIInFyMPNSG0u0iiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1190&quot; height=&quot;898&quot; data-filename=&quot;install.png&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;898&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;result.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kG4dy/dJMcaaSyHA6/5q4p2J5heyRCKdh9AWBfu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kG4dy/dJMcaaSyHA6/5q4p2J5heyRCKdh9AWBfu1/img.png&quot; data-alt=&quot;Create, Insert, Update, Delete, Select 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kG4dy/dJMcaaSyHA6/5q4p2J5heyRCKdh9AWBfu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkG4dy%2FdJMcaaSyHA6%2F5q4p2J5heyRCKdh9AWBfu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;233&quot; data-filename=&quot;result.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Create, Insert, Update, Delete, Select 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 예제 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Connection을 유지해도 괜찮지만 가벼운 SQLite 특성상 매번 접근해줘도 성능에 무리는 없다고 하네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;connectString에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data Source=mydb.db&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언을 통해 mydb.db 파일에 접근이 가능해집니다. (파일이 없다면 자동 생성합니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1776663860524&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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 = &quot;Data Source=mydb.db;Version=3;&quot;;

        public Form1()
        {
            InitializeComponent();
        }

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

                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 = &quot;INSERT INTO Users (Name, Age) VALUES (@name, @age)&quot;;

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

        private List&amp;lt;Data&amp;gt; SelectAll()
        {
            List&amp;lt;Data&amp;gt; dataList = new List&amp;lt;Data&amp;gt;();

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

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

            return dataList;
        }

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

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

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

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

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

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

            List&amp;lt;Data&amp;gt; dataList = SelectAll();
            PrintData(dataList);

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

            dataList = SelectAll();
            PrintData(dataList);

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

        private void PrintData(List&amp;lt;Data&amp;gt; dataList)
        {
            foreach (Data data in dataList)
            {
                textBox1.Text += string.Format(&quot;Id:{0} Name:{1} Age:{2}{3}&quot;, data.Id, data.Name, data.Age, Environment.NewLine);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#/Windows Form</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/125</guid>
      <comments>https://zergling.tistory.com/125#entry125comment</comments>
      <pubDate>Mon, 20 Apr 2026 14:49:29 +0900</pubDate>
    </item>
    <item>
      <title>[C# Windows Form] CustomTreeView 트리뷰 (깜박임 제거, 드래그 앤 드롭, 텍스트 추가 출력 등)</title>
      <link>https://zergling.tistory.com/124</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DGkiy/dJMcacCKE5b/a57Wjk8rQ2pES9S5i82fN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DGkiy/dJMcacCKE5b/a57Wjk8rQ2pES9S5i82fN0/img.png&quot; data-alt=&quot;커스텀 트리뷰어&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DGkiy/dJMcacCKE5b/a57Wjk8rQ2pES9S5i82fN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDGkiy%2FdJMcacCKE5b%2Fa57Wjk8rQ2pES9S5i82fN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;434&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;커스텀 트리뷰어&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tag 값을 옆쪽에 출력하고 깜박임 없는 트리에 대한 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SendMessage를 이용하여 트리뷰 더블버퍼를 켜줘야 반짝임이 없어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 트리뷰어 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[CustomTreeView]&lt;/p&gt;
&lt;pre id=&quot;code_1776056812056&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp_Tree
{
    class CustomTreeView : TreeView
    {
        [DllImport(&quot;user32.dll&quot;)]
        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 &amp;amp; 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 &amp;amp; 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 &amp;amp;&amp;amp; node.Nodes.Count &amp;gt; 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 &amp;amp;&amp;amp; dragNode != null &amp;amp;&amp;amp; !isDragging)
            {
                //일정 거리 이상 움직여야 드래그 (클릭 오작동 방지)
                if (Math.Abs(e.X - dragStartPoint.X) &amp;gt; dragSize.Width || 
                    Math.Abs(e.Y - dragStartPoint.Y) &amp;gt; 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 &amp;amp;&amp;amp; dragNode.Level == 0 &amp;amp;&amp;amp; targetNode != null &amp;amp;&amp;amp; 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;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트리뷰어 사용 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[MainForm]&lt;/p&gt;
&lt;pre id=&quot;code_1776057002712&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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 = @&quot;C:\TestImage\tree_folder_normal.png&quot;;
            string folderSelect = @&quot;C:\TestImage\tree_folder_select.png&quot;;
            string fileNormal = @&quot;C:\TestImage\tree_file_normal.png&quot;;
            string fileSelect = @&quot;C:\TestImage\tree_file_select.png&quot;;

            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(&quot;D4&quot;), 0, 1); //폴더 이미지
            newNode.Tag = &quot;Tag : &quot; + nodeSequence.ToString(&quot;D4&quot;);
            newNode.Checked = true;
            return newNode;
        }

        private TreeNode GetFileNode()
        {
            nodeSequence++;

            TreeNode newNode = new TreeNode(nodeSequence.ToString(&quot;D4&quot;), 2, 3); //파일 이미지
            newNode.Tag = &quot;Tag : &quot; + nodeSequence.ToString(&quot;D4&quot;);
            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 &amp;gt; 0)
                {
                    selectedNode = customTreeView1.Nodes[0];
                }
                else
                {
                    selectedNode = GetFolderNode();
                    customTreeView1.Nodes.Add(selectedNode);
                }
            }

            for (int i = 0; i &amp;lt; 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();
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#/Windows Form</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/124</guid>
      <comments>https://zergling.tistory.com/124#entry124comment</comments>
      <pubDate>Mon, 13 Apr 2026 14:10:11 +0900</pubDate>
    </item>
    <item>
      <title>[C# Windows Form] 디자이너에서 커스텀 필드 생성하기</title>
      <link>https://zergling.tistory.com/123</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;button1은 CustomPanel 하위 목록으로 들어가면서 추가 될 필드를 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TableLayout 에서 Row, RowSpan 등의 속성이 추가되는 것이 궁금하여 찾아본 내용 정리입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;697&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QgVYl/dJMcagkk56l/wDYjc5q3upkEBNMLkKKDNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QgVYl/dJMcagkk56l/wDYjc5q3upkEBNMLkKKDNK/img.png&quot; data-alt=&quot;CustomPanel 하위 목록에 대해서 디자이너에서 보여줄 필드를 생성 가능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QgVYl/dJMcagkk56l/wDYjc5q3upkEBNMLkKKDNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQgVYl%2FdJMcagkk56l%2FwDYjc5q3upkEBNMLkKKDNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1108&quot; height=&quot;697&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;697&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CustomPanel 하위 목록에 대해서 디자이너에서 보여줄 필드를 생성 가능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Panel 외에도 다른 컨트롤을 상속받아서도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로 Dictionary 처리를 통해 값을 별도로 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ProvideProperty 는 여러 개를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IExtenderProvider 를 상속받아야 하며 Setter와 Getter를 구현해주어야 사용 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 예시 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1772719386990&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using System.ComponentModel; //IExtenderProvider
using System.Windows.Forms;

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

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

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

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

        [DisplayName(&quot;CustomField&quot;)] //없으면 클래스명의 필드로 이름 지정됨.
        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;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C#/Windows Form</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/123</guid>
      <comments>https://zergling.tistory.com/123#entry123comment</comments>
      <pubDate>Thu, 5 Mar 2026 23:05:07 +0900</pubDate>
    </item>
    <item>
      <title>[C#] 날짜 지난 파일 제거 (FileInfo, DateTime)</title>
      <link>https://zergling.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1764773533615&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.IO;

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

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

                //A &amp;lt; B = 1, A == B = 0, A &amp;gt; B = -1
                if (DateTime.Compare(now, fileInfo.LastWriteTime) &amp;gt; 0) //기준보다 오래된 파일 삭제
                {
                    fileInfo.Delete();
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#/Console</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/122</guid>
      <comments>https://zergling.tistory.com/122#entry122comment</comments>
      <pubDate>Wed, 3 Dec 2025 23:52:40 +0900</pubDate>
    </item>
    <item>
      <title>[C# Windows Form] PrivateFontCollection 외부 폰트 파일 적용</title>
      <link>https://zergling.tistory.com/121</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6U4YA/dJMcacn1CXW/y7wmUqaGh7NKnB6PLj8Tt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6U4YA/dJMcacn1CXW/y7wmUqaGh7NKnB6PLj8Tt1/img.png&quot; data-alt=&quot;폰트 적용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6U4YA/dJMcacn1CXW/y7wmUqaGh7NKnB6PLj8Tt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6U4YA%2FdJMcacn1CXW%2Fy7wmUqaGh7NKnB6PLj8Tt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;269&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;482&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;폰트 적용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제로 창원 단감 폰트 사용..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChangwonDangamAsac-Bold_0712.ttf&lt;/p&gt;
&lt;pre id=&quot;code_1764318742471&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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, &quot;ChangwonDangamAsac-Bold_0712.ttf&quot;));

            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 &amp;gt; 0) //panel, groupbox 와 같이 하위 컨트롤이 더 있는 경우도 적용시켜줍니다.
                {
                    SetControlFont(child);
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#/Windows Form</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/121</guid>
      <comments>https://zergling.tistory.com/121#entry121comment</comments>
      <pubDate>Fri, 28 Nov 2025 17:33:49 +0900</pubDate>
    </item>
    <item>
      <title>[C# Windows Form] FormBorderStyle None 에 대한 기본 클래스</title>
      <link>https://zergling.tistory.com/120</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1) 상하좌우 크기 조절 기능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 최대화, 최소화, 종료 버튼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 타이틀 버튼 설정 (마우스 클릭, 더블 클릭)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TestForm (FormParent 상속 받아서 기능 사용.)&lt;/p&gt;
&lt;pre id=&quot;code_1764312467809&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace FormStyleNoneResize
{
    public partial class TestForm : FormParent
    {
        public TestForm()
        {
            InitializeComponent();
        }
        
        private void Form1_Load(object sender, System.EventArgs e)
        {
            SetTitle(label_title); //마우스 이동 이벤트 등록
            SetTitle(panel_title); //마우스 이동 이벤트 등록

            SetCloseButton(customButton_close); //닫기 버튼
            SetMaximumButton(customButton_max); //최대화 버튼
            SetMinimumButton(customButton_min); //최소화 버튼
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FormParent Class&lt;/p&gt;
&lt;pre id=&quot;code_1764312210153&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;
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;

        private List&amp;lt;Control&amp;gt; titleList = new List&amp;lt;Control&amp;gt;();
        private Button closeButton = null;
        private Button maxButton = null;
        private Button minButton = null;

        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 &amp;lt;= grip;
                    bool right = this.Width - grip &amp;lt;= position.X;
                    bool top = position.Y &amp;lt;= grip;
                    bool bottom = this.Height - grip &amp;lt;= position.Y;

                    if (left &amp;amp;&amp;amp; top) m.Result = (IntPtr)HTTOPLEFT;
                    else if (right &amp;amp;&amp;amp; top) m.Result = (IntPtr)HTTOPRIGHT;
                    else if (left &amp;amp;&amp;amp; bottom) m.Result = (IntPtr)HTBOTTOMLEFT;
                    else if (right &amp;amp;&amp;amp; 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)
        {
            titleList.Add(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 &amp;amp; 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();
        }

        protected override void OnFormClosed(FormClosedEventArgs e)
        {
            foreach (Control control in titleList)
            {
                control.MouseDoubleClick -= titleControl_MouseDoubleClick;
                control.MouseDown -= titleControl_MouseDown;
                control.MouseMove -= titleControl_MouseMove;
            }

            if (closeButton != null)
            {
                closeButton.Click -= button_close_Click;
            }

            if (closeButton != null)
            {
                maxButton.Click -= button_maximum_Click;
            }

            if (closeButton != null)
            {
                minButton.Click -= button_minimum_Click;
            }

            base.OnFormClosed(e);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NativeWindow 추가&lt;/p&gt;
&lt;pre id=&quot;code_1781848720264&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Windows.Forms;

namespace NativeWindowTest
{
    public class TransparentNativeWindow : NativeWindow, IDisposable
    {
        private const int WM_NCHITTEST = 0x84;
        private const int HTTRANSPARENT = -1;

        public bool Enabled { get; set; } = true;

        public TransparentNativeWindow(Control control) //등록 (외부 리스트로 관리)
        {
            AssignHandle(control.Handle);
        }

        protected override void WndProc(ref Message m) //마우스 입력에 대해 패스
        {
            if (Enabled &amp;amp;&amp;amp; m.Msg == WM_NCHITTEST)
            {
                m.Result = (IntPtr)HTTRANSPARENT;
                return;
            }

            base.WndProc(ref m);
        }

        public void Dispose()
        {
            ReleaseHandle();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#/Windows Form</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/120</guid>
      <comments>https://zergling.tistory.com/120#entry120comment</comments>
      <pubDate>Fri, 28 Nov 2025 15:48:10 +0900</pubDate>
    </item>
    <item>
      <title>[C# Windows Form] Custom Button Class - 이미지 버튼 생성 (hover, press, normal, focus)</title>
      <link>https://zergling.tistory.com/119</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 이미지 및 문자 색상을 나타낼 수 있는 버튼을 만드는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마우스 이동 시 이벤트가 자동으로 발생되어 지정한 이미지가 나타나도록 가능합니다. (텍스트 등 필요한 기능은 직접 추가!)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 53.2559%; height: 152px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 142px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 142px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;normal.png&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs60Jw/dJMcacn1fim/86URQ4EpuJNTtCzLgjsCH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs60Jw/dJMcacn1fim/86URQ4EpuJNTtCzLgjsCH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs60Jw/dJMcacn1fim/86URQ4EpuJNTtCzLgjsCH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs60Jw%2FdJMcacn1fim%2F86URQ4EpuJNTtCzLgjsCH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;184&quot; data-filename=&quot;normal.png&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 70.9189%; height: 142px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;hover.png&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKetvD/dJMcacn1fip/IdyDZelKBkvxQgPpQZLsD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKetvD/dJMcacn1fip/IdyDZelKBkvxQgPpQZLsD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKetvD/dJMcacn1fip/IdyDZelKBkvxQgPpQZLsD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKetvD%2FdJMcacn1fip%2FIdyDZelKBkvxQgPpQZLsD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;184&quot; data-filename=&quot;hover.png&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 10px;&quot;&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;press.png&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oEwCp/dJMcaiaHqON/28qqq5flx1f9yitUQVopq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oEwCp/dJMcaiaHqON/28qqq5flx1f9yitUQVopq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oEwCp/dJMcaiaHqON/28qqq5flx1f9yitUQVopq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoEwCp%2FdJMcaiaHqON%2F28qqq5flx1f9yitUQVopq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;183&quot; data-filename=&quot;press.png&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 70.9189%; height: 10px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;disable.png&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb2Hd5/dJMcabiluRB/Da8dDAv1T1iTuwKrKFXXa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb2Hd5/dJMcabiluRB/Da8dDAv1T1iTuwKrKFXXa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb2Hd5/dJMcabiluRB/Da8dDAv1T1iTuwKrKFXXa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb2Hd5%2FdJMcabiluRB%2FDa8dDAv1T1iTuwKrKFXXa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;187&quot; data-filename=&quot;disable.png&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 Button을 상속받은 아래의 클래스를 생성 후&lt;b&gt; &quot;프로젝트 빌드&quot;&lt;/b&gt; 를 해주어야 사용 가능해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Image_Disable, Image_Disable_Color ... 에 대해 이미지 및 텍스트 색상 지정을 해주면 끝!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;MyButton_2.png&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;1001&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d820Oy/dJMcadUH6I2/C3ow0LYX6Coe9YUp5W0De1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d820Oy/dJMcadUH6I2/C3ow0LYX6Coe9YUp5W0De1/img.png&quot; data-alt=&quot;이미지 등록하는 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d820Oy/dJMcadUH6I2/C3ow0LYX6Coe9YUp5W0De1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd820Oy%2FdJMcadUH6I2%2FC3ow0LYX6Coe9YUp5W0De1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1902&quot; height=&quot;1001&quot; data-filename=&quot;MyButton_2.png&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;1001&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 등록하는 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1764231036992&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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 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; //이미지 보여주는 형태

            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); //마우스 위치에 따라 상태 변경
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 버전&lt;/p&gt;
&lt;pre id=&quot;code_1777365623143&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace MyButton
{
    class CustomButton : Button
    {
        public uint BorderRadius
        {
            get
            {
                return borderRadius;
            }
            set
            {
                borderRadius = value;
                UpdateRegion();
                this.Refresh();
            }
        }
        private uint borderRadius = 20;

        public uint BorderSize
        {
            get
            {
                return borderSize;
            }
            set
            {
                borderSize = value;
                UpdateRegion();
                this.Refresh();
            }
        }
        private uint borderSize = 1;
        
        public Color NormalBackColor
        {
            get
            {
                return backColor;
            }
            set
            {
                backColor = value;
                this.BackColor = value;
            }
        }
        private Color backColor = Color.White;

        public Color NormalTextColor
        {
            get
            {
                return foreColor;
            }
            set
            {
                foreColor = value;
                this.ForeColor = value;
            }
        }
        private Color foreColor = Color.Black;

        public Color FocusBackColor { get; set; } = Color.ForestGreen;
        public Color FocusTextColor { get; set; } = Color.Black;
        public Color PressBackColor { get; set; } = Color.SteelBlue;
        public Color PressTextColor { get; set; } = Color.White;
        public Color DisableBackColor { get; set; } = Color.LightGray;
        public Color DisableTextColor { get; set; } = Color.DarkGray;

        private bool isMouseDown;

        public CustomButton()
        {
            this.FlatStyle = FlatStyle.Flat;
            this.FlatAppearance.BorderSize = 0;
        }

        protected override void OnEnabledChanged(EventArgs e)
        {
            base.OnEnabledChanged(e);

            if (!Enabled)
            {
                this.BackColor = DisableBackColor;
                this.ForeColor = DisableTextColor;
            }
            else
            {
                this.BackColor = NormalBackColor;
                this.ForeColor = NormalTextColor;
            }
            this.Invalidate();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            UpdateRegion();
        }

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

            if (isMouseDown)
            {
                return;
            }

            this.BackColor = FocusBackColor;
            this.ForeColor = FocusTextColor;
            this.Invalidate();
        }

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

            this.BackColor = NormalBackColor;
            this.ForeColor = NormalTextColor;
            this.Invalidate();
        }

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

            if (e.Button != MouseButtons.Left)
            {
                return;
            }

            this.BackColor = PressBackColor;
            this.ForeColor = PressTextColor;
            this.Invalidate();

            isMouseDown = true;
        }

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

            if (e.Button != MouseButtons.Left)
            {
                return;
            }

            this.BackColor = NormalBackColor;
            this.ForeColor = NormalTextColor;
            this.Invalidate();

            isMouseDown = false;
        }

        protected override void OnPaint(PaintEventArgs pevent)
        {
            pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            pevent.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
            pevent.Graphics.Clear(Parent.BackColor);

            Rectangle rect = ClientRectangle;
            rect.Inflate(-1, -1);

            Color fillColor = this.BackColor;
            
            using (SolidBrush brush = new SolidBrush(fillColor))
            {
                using (GraphicsPath path = GetRoundPath(rect, (int)borderRadius))
                {
                    pevent.Graphics.FillPath(brush, path);

                    if (BorderSize &amp;gt; 0)
                    {
                        using (Pen pen = new Pen(fillColor, BorderSize + 0.5f))
                        {
                            pen.Alignment = PenAlignment.Inset;
                            pevent.Graphics.DrawPath(pen, path);
                        }
                    }
                }

                //센터쪽으로 텍스트 출력
                TextRenderer.DrawText(pevent.Graphics,
                                      Text,
                                      Font,
                                      rect,
                                      this.ForeColor,
                                      TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
            }
        }

        private void UpdateRegion()
        {
            Rectangle rect = ClientRectangle;
            rect.Width -= 1;
            rect.Height -= 1;

            using (GraphicsPath path = GetRoundPath(rect, (int)borderRadius))
            {
                Region oldRegion = this.Region;
                this.Region = new Region(path);
                oldRegion?.Dispose();
            }
        }

        private GraphicsPath GetRoundPath(Rectangle rect, int radius2)
        {
            GraphicsPath path = new GraphicsPath();

            int x = rect.X;
            int y = rect.Y;
            int width = rect.Width;
            int height = rect.Height;

            int radius = radius2 * 2;

            path.StartFigure();

            path.AddArc(x, y, radius, radius, 180, 90);
            path.AddArc(x + width - radius, y, radius, radius, 270, 90);
            path.AddArc(x + width - radius, y + height - radius, radius, radius, 0, 90);
            path.AddArc(x, y + height - radius, radius, radius, 90, 90);

            path.CloseAllFigures();

            return path;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C#/Windows Form</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/119</guid>
      <comments>https://zergling.tistory.com/119#entry119comment</comments>
      <pubDate>Thu, 27 Nov 2025 17:11:47 +0900</pubDate>
    </item>
    <item>
      <title>[C# Windows Form] 섬네일 만들기 (ListView, ListBox)</title>
      <link>https://zergling.tistory.com/118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ListView 혹은 ListBox를 이용한 섬네일 만드는 클래스 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class ThumbnailView : ListView&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class ThumbnailBox : ListBox&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속을 통해 만들었고&amp;nbsp;필요한 함수는 커스텀해서 사용 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ListView는 가로로도 확장이 가능하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ListBox는 세로만 가능하게 만들었고, 사이즈가 가변적으로 변경됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 결과 및 사용 방법.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnAEzx/dJMcag4Xg3o/8YOXcdICF9FA0k4LpFV0Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnAEzx/dJMcag4Xg3o/8YOXcdICF9FA0k4LpFV0Kk/img.png&quot; data-alt=&quot;ListView, ListBox 베이스 섬네일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnAEzx/dJMcag4Xg3o/8YOXcdICF9FA0k4LpFV0Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnAEzx%2FdJMcag4Xg3o%2F8YOXcdICF9FA0k4LpFV0Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;486&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ListView, ListBox 베이스 섬네일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763337112586&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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 = @&quot;C:\Test&quot;; //폴더 경로
            string[] files = Directory.GetFiles(folder);

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

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

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

                Invoke((MethodInvoker)delegate
                {
                    button_add.Enabled = true; //추가 버튼
                });
            });
            
            //GetSelectedPathList, RemoveSelectedThumbnail, RemoveAllThumbnail 있음.
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ListView 클래스 (빌드 후 사용)&lt;/p&gt;
&lt;pre id=&quot;code_1763337376899&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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(() =&amp;gt;
                {
                    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&amp;lt;string&amp;gt; GetSelectedPathList() //선택 된 경로 반환
        {
            if (this.SelectedIndices == null)
            {
                return new List&amp;lt;string&amp;gt;();
            }

            List&amp;lt;string&amp;gt; pathList = new List&amp;lt;string&amp;gt;();

            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 &amp;lt;= 0)
            {
                return;
            }

            this.BeginUpdate();

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

            this.EndUpdate();
        }

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

            this.BeginUpdate();

            for (int i = this.Items.Count - 1; i &amp;gt;= 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);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ListBox 클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ ItemHeight가 변경 되면 공백이 남는 경우가 생깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에따라 고정크기 panel 하위에 listbox를 생성하고, 현재 보여지는 크기에 맞추어 ItemHeight가 가변적으로 변경됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1763337656377&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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&amp;lt;ThumbnailInfo&amp;gt; thumbnailInfoList = new List&amp;lt;ThumbnailInfo&amp;gt;();
        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 &amp;lt; 0)
            {
                return;
            }

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

            if ((e.State &amp;amp; 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&amp;lt;string&amp;gt; GetSelectedPathList() //선택 된 경로 반환
        {
            if (listBox.SelectedIndices == null)
            {
                return new List&amp;lt;string&amp;gt;();
            }

            List&amp;lt;string&amp;gt; pathList = new List&amp;lt;string&amp;gt;();

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

            return pathList;
        }

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

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

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

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

            listBox.BeginUpdate();

            //뒷쪽부터 삭제
            for (int i = listBox.Items.Count - 1; i &amp;gt;= 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);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#/Windows Form</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/118</guid>
      <comments>https://zergling.tistory.com/118#entry118comment</comments>
      <pubDate>Mon, 17 Nov 2025 09:13:56 +0900</pubDate>
    </item>
    <item>
      <title>[C# Windows Form] ZipFile 다루기 - 압축 및 해제</title>
      <link>https://zergling.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;C# 에서 기본적으로 제공되는 압축 해제 기능으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.IO.Compression 가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화는 지원하지 않지만 기본 내장되어 있고 사용도 편리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ZipFile 클래스 사용을 위해선 System.IO.Compression.FileSystem 참조가 필요합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JWqz2/btsQ4B2fSQ7/tIcmm2PU7cMVG743K2tUOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JWqz2/btsQ4B2fSQ7/tIcmm2PU7cMVG743K2tUOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JWqz2/btsQ4B2fSQ7/tIcmm2PU7cMVG743K2tUOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJWqz2%2FbtsQ4B2fSQ7%2FtIcmm2PU7cMVG743K2tUOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1160&quot; height=&quot;553&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759753733020&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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, &quot;TestFiles&quot;);
            string savePath = Path.Combine(workFolder, &quot;Zipfile.zip&quot;);

            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, &quot;Zipfile.zip&quot;);
            string saveFolder = Path.Combine(workFolder, &quot;unZip&quot;);

            if (Directory.Exists(saveFolder)) //기존 파일은 제거
            {
                Directory.Delete(saveFolder, true);
            }
            
            ZipFile.ExtractToDirectory(targetPath, saveFolder);
            Process.Start(workFolder); //폴더 열어서 확인
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbz5vb/btsQ3bwAds3/QKsjZnm3zeHkUFhyzFqKv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbz5vb/btsQ3bwAds3/QKsjZnm3zeHkUFhyzFqKv1/img.png&quot; data-alt=&quot;파일 압축 및 풀기 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbz5vb/btsQ3bwAds3/QKsjZnm3zeHkUFhyzFqKv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdbz5vb%2FbtsQ3bwAds3%2FQKsjZnm3zeHkUFhyzFqKv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1037&quot; height=&quot;605&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파일 압축 및 풀기 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 밖에도 다른 라이브러리들이 있는데, NuGet 에서 받아서 사용이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPT에서 제공한 표를 남겨두겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- GPT 정리 내용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브 러리 암호화 포맷 속도 최신 유지장점 요약 (2025년 10월 06일 기준!)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 120px;&quot; border=&quot;1&quot; data-end=&quot;3042&quot; data-start=&quot;2613&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot;&gt;&lt;b&gt;라이브러리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot;&gt;&lt;b&gt;암호화&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot;&gt;&lt;b&gt;포맷&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot;&gt;&lt;b&gt;속도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot;&gt;&lt;b&gt;최신 유지&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot;&gt;&lt;b&gt;장점 요약&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;2788&quot; data-start=&quot;2725&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2749&quot; data-start=&quot;2725&quot;&gt;System.IO.Compression&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2753&quot; data-start=&quot;2749&quot;&gt;❌&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2759&quot; data-start=&quot;2753&quot;&gt;ZIP&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2766&quot; data-start=&quot;2759&quot;&gt;✅ 빠름&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2773&quot; data-start=&quot;2766&quot;&gt;✅ 최신&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2788&quot; data-start=&quot;2773&quot;&gt;.NET 내장, 간단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;2851&quot; data-start=&quot;2789&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2813&quot; data-start=&quot;2789&quot;&gt;DotNetZip (Ionic.Zip)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2817&quot; data-start=&quot;2813&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2823&quot; data-start=&quot;2817&quot;&gt;ZIP&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2830&quot; data-start=&quot;2823&quot;&gt;⚪ 보통&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2838&quot; data-start=&quot;2830&quot;&gt;❌ 구버전&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2851&quot; data-start=&quot;2838&quot;&gt;암호 ZIP 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;2917&quot; data-start=&quot;2852&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2866&quot; data-start=&quot;2852&quot;&gt;SharpZipLib&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2870&quot; data-start=&quot;2866&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2889&quot; data-start=&quot;2870&quot;&gt;ZIP, TAR, GZIP 등&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2896&quot; data-start=&quot;2889&quot;&gt;⚪ 보통&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2907&quot; data-start=&quot;2896&quot;&gt;✅ 유지보수 중&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2917&quot; data-start=&quot;2907&quot;&gt;다양한 포맷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;2985&quot; data-start=&quot;2918&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2934&quot; data-start=&quot;2918&quot;&gt;SharpCompress&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2943&quot; data-start=&quot;2934&quot;&gt;⚠️ 제한적&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2960&quot; data-start=&quot;2943&quot;&gt;ZIP, 7z, TAR 등&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2967&quot; data-start=&quot;2960&quot;&gt;⚪ 보통&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2974&quot; data-start=&quot;2967&quot;&gt;✅ 최신&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2985&quot; data-start=&quot;2974&quot;&gt;스트리밍 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;3042&quot; data-start=&quot;2986&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3002&quot; data-start=&quot;2986&quot;&gt;SevenZipSharp&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3010&quot; data-start=&quot;3002&quot;&gt;✅ AES&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3015&quot; data-start=&quot;3010&quot;&gt;7z&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3023&quot; data-start=&quot;3015&quot;&gt;⚠️ 느림&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3031&quot; data-start=&quot;3023&quot;&gt;⚪ 부분적&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3042&quot; data-start=&quot;3031&quot;&gt;고압축, 보안&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DotNetZip 간단 사용법 예시&lt;/p&gt;
&lt;pre id=&quot;code_1759753959603&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Ionic.Zip;

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

// 해제
using (var zip = ZipFile.Read(@&quot;C:\Result.zip&quot;))
{
    zip.Password = &quot;1234&quot;;
    zip.ExtractAll(@&quot;C:\Extracted&quot;, ExtractExistingFileAction.OverwriteSilently);
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#/Windows Form</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/117</guid>
      <comments>https://zergling.tistory.com/117#entry117comment</comments>
      <pubDate>Mon, 6 Oct 2025 21:32:49 +0900</pubDate>
    </item>
    <item>
      <title>[C#] Stopwatch 경과 시간 확인하기 (tick, milliseconds, timespan)</title>
      <link>https://zergling.tistory.com/116</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;DateTime 으로 시간을 확인 할 수 있지만, Stopwatch 를 통해 경과 시간을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 요소를 통해 아래의 방법대로 표현도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czBZ2z/btsQTCahqxP/yQSskiuVI21KpQjdHX9GwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czBZ2z/btsQTCahqxP/yQSskiuVI21KpQjdHX9GwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czBZ2z/btsQTCahqxP/yQSskiuVI21KpQjdHX9GwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczBZ2z%2FbtsQTCahqxP%2FyQSskiuVI21KpQjdHX9GwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;978&quot; height=&quot;508&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759147956985&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;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 &amp;lt; 5000) //5초
            {
                try
                {
                    using (TcpClient tcpClient = new TcpClient()) //연결만 확인.
                    {
                        Task task = tcpClient.ConnectAsync(&quot;127.0.0.1&quot;, 8080); //연결할 주소
                        if (task.Wait(3000) &amp;amp;&amp;amp; tcpClient.Connected) //연결 확인
                        {
                            tcpClient.Close();
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    //Console.WriteLine(ex); //서버 없으면 오류나서 주석처리
                }

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

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

                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#/Console</category>
      <author>스타크래프트 좋아하는 사람</author>
      <guid isPermaLink="true">https://zergling.tistory.com/116</guid>
      <comments>https://zergling.tistory.com/116#entry116comment</comments>
      <pubDate>Mon, 29 Sep 2025 21:20:14 +0900</pubDate>
    </item>
  </channel>
</rss>