첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형

TestProject.zip
다운로드

▶ IconFactory.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace TestProject
{
    /// <summary>
    /// 아이콘 헬퍼
    /// </summary>
    public class IconHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Import
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region SH 변경 통지하기 - SHChangeNotify(eventID, flag, item1Handle, item2Handle)

        /// <summary>
        /// SH 변경 통지하기
        /// </summary>
        /// <param name="eventID">이벤트 ID</param>
        /// <param name="flag">플래그</param>
        /// <param name="item1Handle">항목 1 핸들</param>
        /// <param name="item2Handle">항목 2 핸들</param>
        [DllImport("shell32.dll")]
        private static extern void SHChangeNotify
        (
            SHChangeNotifyEventID eventID,
            SHChangeNotifyFlag    flag,
            IntPtr               item1Handle,
            IntPtr               item2Handle
        );

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region SH 변경 통지 이벤트 ID - SHChangeNotifyEventID

        /// <summary>
        /// SH 변경 통지 이벤트 ID
        /// </summary>
        [Flags]
        private enum SHChangeNotifyEventID
        {
            /// <summary>
            /// SHCNE_ALLEVENTS
            /// </summary>
            SHCNE_ALLEVENTS = 0x7FFFFFFF,

            /// <summary>
            /// SHCNE_ASSOCCHANGED
            /// </summary>
            SHCNE_ASSOCCHANGED = 0x08000000,

            /// <summary>
            /// SHCNE_ATTRIBUTES
            /// </summary>
            SHCNE_ATTRIBUTES = 0x00000800,

            /// <summary>
            /// SHCNE_CREATE
            /// </summary>
            SHCNE_CREATE = 0x00000002,

            /// <summary>
            /// SHCNE_DELETE
            /// </summary>
            SHCNE_DELETE = 0x00000004,

            /// <summary>
            /// SHCNE_DRIVEADD
            /// </summary>
            SHCNE_DRIVEADD = 0x00000100,

            /// <summary>
            /// SHCNE_DRIVEADDGUI
            /// </summary>
            SHCNE_DRIVEADDGUI = 0x00010000,

            /// <summary>
            /// SHCNE_DRIVEREMOVED
            /// </summary>
            SHCNE_DRIVEREMOVED = 0x00000080,

            /// <summary>
            /// SHCNE_EXTENDED_EVENT
            /// </summary>
            SHCNE_EXTENDED_EVENT = 0x04000000,

            /// <summary>
            /// SHCNE_FREESPACE
            /// </summary>
            SHCNE_FREESPACE = 0x00040000,

            /// <summary>
            /// SHCNE_MEDIAINSERTED
            /// </summary>
            SHCNE_MEDIAINSERTED = 0x00000020,

            /// <summary>
            /// SHCNE_MEDIAREMOVED
            /// </summary>
            SHCNE_MEDIAREMOVED = 0x00000040,

            /// <summary>
            /// SHCNE_MKDIR
            /// </summary>
            SHCNE_MKDIR = 0x00000008,

            /// <summary>
            /// SHCNE_NETSHARE
            /// </summary>
            SHCNE_NETSHARE = 0x00000200,

            /// <summary>
            /// SHCNE_NETUNSHARE
            /// </summary>
            SHCNE_NETUNSHARE = 0x00000400,

            /// <summary>
            /// SHCNE_RENAMEFOLDER
            /// </summary>
            SHCNE_RENAMEFOLDER = 0x00020000,

            /// <summary>
            /// SHCNE_RENAMEITEM
            /// </summary>
            SHCNE_RENAMEITEM = 0x00000001,

            /// <summary>
            /// SHCNE_RMDIR
            /// </summary>
            SHCNE_RMDIR = 0x00000010,

            /// <summary>
            /// SHCNE_SERVERDISCONNECT
            /// </summary>
            SHCNE_SERVERDISCONNECT = 0x00004000,

            /// <summary>
            /// SHCNE_UPDATEDIR
            /// </summary>
            SHCNE_UPDATEDIR = 0x00001000,

            /// <summary>
            /// SHCNE_UPDATEIMAGE
            /// </summary>
            SHCNE_UPDATEIMAGE = 0x00008000,
        }

        #endregion
        #region SH 변경 통지 플래그 - SHChangeNotifyFlag

        /// <summary>
        /// SH 변경 통지 플래그
        /// </summary>
        private enum SHChangeNotifyFlag : uint
        {
            /// <summary>
            /// SHCNF_IDLIST
            /// </summary>
            SHCNF_IDLIST = 0x0000,

            /// <summary>
            /// SHCNF_PATHA
            /// </summary>
            SHCNF_PATHA = 0x0001,

            /// <summary>
            /// SHCNF_PRINTERA
            /// </summary>
            SHCNF_PRINTERA = 0x0002,

            /// <summary>
            /// SHCNF_DWORD
            /// </summary>
            SHCNF_DWORD = 0x0003,

            /// <summary>
            /// SHCNF_PATHW
            /// </summary>
            SHCNF_PATHW = 0x0005,

            /// <summary>
            /// SHCNF_PRINTERW
            /// </summary>
            SHCNF_PRINTERW = 0x0006,

            /// <summary>
            /// SHCNF_TYPE
            /// </summary>
            SHCNF_TYPE = 0x00FF,

            /// <summary>
            /// SHCNF_FLUSH
            /// </summary>
            SHCNF_FLUSH = 0x1000,

            /// <summary>
            /// SHCNF_FLUSHNOWAIT
            /// </summary>
            SHCNF_FLUSHNOWAIT = 0x2000,

            /// <summary>
            /// SHCNF_PATH
            /// </summary>
            SHCNF_PATH = SHCNF_PATHW,

            /// <summary>
            /// SHCNF_PRINTER
            /// </summary>
            SHCNF_PRINTER = SHCNF_PRINTERW
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 최대 아이콘 너비
        /// </summary>
        public const int MAXIMUM_ICON_WIDTH = 256;

        /// <summary>
        /// 최대 아이콘 높이
        /// </summary>
        public const int MAXIMUM_ICON_HEIGHT = 256;

        /// <summary>
        /// 헤더 예약
        /// </summary>
        private const ushort HEADER_RESERVED = 0;

        /// <summary>
        /// 헤더 아이콘 타입
        /// </summary>
        private const ushort HEADER_ICON_TYPE = 1;

        /// <summary>
        /// 헤더 길이
        /// </summary>
        private const byte HEADER_LENGTH = 6;

        /// <summary>
        /// 엔트리 예약
        /// </summary>
        private const byte ENTRY_RESERVED = 0;

        /// <summary>
        /// 엔트리 길이
        /// </summary>
        private const byte ENTRY_LENGTH = 16;

        /// <summary>
        /// 팔레트 내 PNG 색상 수
        /// </summary>
        private const byte PNG_COLOR_COUNT_IN_PALETTE = 0;

        /// <summary>
        /// PNG 색상 플레인 수
        /// </summary>
        private const ushort PNG_COLOR_PLANE_COUNT = 1;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 저장하기 - Save(bitmapEnumerable, stream, notifySystem)

        /// <summary>
        /// 저장하기
        /// </summary>
        /// <param name="bitmapEnumerable">비트맵 열거 가능형</param>
        /// <param name="stream">스트림</param>
        /// <param name="notifySystem">시스템 통지 여부</param>
        public static void Save(IEnumerable<Bitmap> bitmapEnumerable, Stream stream, bool notifySystem)
        {
            Bitmap[] bitmapArray = bitmapEnumerable.OrderBy(i => i.Width).ThenBy(i => i.Height).ToArray();

            using(BinaryWriter writer = new BinaryWriter(stream))
            {
                writer.Write(HEADER_RESERVED);
                writer.Write(HEADER_ICON_TYPE);
                writer.Write((ushort)bitmapArray.Length);

                Dictionary<uint, byte[]> dictionary = new Dictionary<uint, byte[]>();

                uint lengthSummary = 0;

                uint baseOffset = (uint)(HEADER_LENGTH * ENTRY_LENGTH * bitmapArray.Length);

                for(int i = 0; i < bitmapArray.Length; i++)
                {
                    Bitmap bitmap = bitmapArray[i];

                    byte[] byteArray = GetBitmapByteArray(bitmap);

                    uint offset = baseOffset + lengthSummary;

                    writer.Write(GetIconWidth(bitmap));
                    writer.Write(GetIconHeight(bitmap));
                    writer.Write(PNG_COLOR_COUNT_IN_PALETTE);
                    writer.Write(ENTRY_RESERVED);
                    writer.Write(PNG_COLOR_PLANE_COUNT);
                    writer.Write((ushort)Image.GetPixelFormatSize(bitmap.PixelFormat));
                    writer.Write((uint)byteArray.Length);
                    writer.Write(offset);

                    lengthSummary += (uint)byteArray.Length;

                    dictionary.Add(offset, byteArray);
                }

                foreach(KeyValuePair<uint, byte[]> keyValuePair in dictionary)
                {
                    writer.BaseStream.Seek(keyValuePair.Key, SeekOrigin.Begin);

                    writer.Write(keyValuePair.Value);
                }
            }

            if(notifySystem)
            {
                SHChangeNotify
                (
                    SHChangeNotifyEventID.SHCNE_ASSOCCHANGED,
                    SHChangeNotifyFlag.SHCNF_IDLIST,
                    IntPtr.Zero,
                    IntPtr.Zero
                );
            }
        }

        #endregion
        #region 저장하기 - Save(bitmapEnumerable)

        /// <summary>
        /// 저장하기
        /// </summary>
        /// <param name="bitmapEnumerable">비트맵 열거 가능형</param>
        /// <returns>아이콘</returns>
        public static Icon Save(IEnumerable<Bitmap> bitmapEnumerable)
        {
            using(MemoryStream stream1 = new MemoryStream())
            {
                Save(bitmapEnumerable, stream1, false);

                using(MemoryStream stream2 = new MemoryStream(stream1.ToArray()))
                {
                    return new Icon(stream2);
                }
            }
        }

        #endregion
        #region 저장하기- Save(bitmapEnumerable, filePath, notifySystem)

        /// <summary>
        /// 저장하기
        /// </summary>
        /// <param name="bitmapEnumerable">비트맵 열거 가능형</param>
        /// <param name="filePath">파일 경로</param>
        /// <param name="notifySystem">시스템 통지 여부</param>
        public static void Save(IEnumerable<Bitmap> bitmapEnumerable, string filePath, bool notifySystem)
        {
            using(FileStream stream = new FileStream(filePath, FileMode.Create))
            {
                Save(bitmapEnumerable, stream, notifySystem);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 비트맵 바이트 배열 구하기 - GetBitmapByteArray(bitmap)

        /// <summary>
        /// 비트맵 바이트 배열 구하기
        /// </summary>
        /// <param name="bitmap">비트맵</param>
        /// <returns>비트맵 바이트 배열</returns>
        private static byte[] GetBitmapByteArray(Bitmap bitmap)
        {
            using(MemoryStream stream = new MemoryStream())
            {
                bitmap.Save(stream, ImageFormat.Png);

                return stream.ToArray();
            }
        }

        #endregion
        #region 아이콘 너비 구하기 - GetIconWidth(bitmap)

        /// <summary>
        /// 아이콘 너비 구하기
        /// </summary>
        /// <param name="bitmap">비트맵</param>
        /// <returns>아이콘 너비</returns>
        private static byte GetIconWidth(Bitmap bitmap)
        {
            if(bitmap.Width >= MAXIMUM_ICON_WIDTH)
            {
                return 0;
            }

            return (byte)bitmap.Width;
        }

        #endregion
        #region 아이콘 높이 구하기 - GetIconHeight(bitmap)

        /// <summary>
        /// 아이콘 높이 구하기
        /// </summary>
        /// <param name="bitmap">비트맵</param>
        /// <returns>아이콘 높이</returns>
        private static byte GetIconHeight(Bitmap bitmap)
        {
            if(bitmap.Height >= MAXIMUM_ICON_HEIGHT)
            {
                return 0;
            }

            return (byte)bitmap.Height;
        }

        #endregion
    }
}

 

728x90

 

▶ MainForm.cs

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

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            Load                      += Form_Load;
            this.openMenuItem.Click   += openMenuItem_Click;
            this.saveAsMenuItem.Click += saveAsMenuItem_Click;
            this.exitMenuItem.Click   += exitMenuItem_Click;
            this.clearMenuItem.Click  += clearMenuItem_Click;
            this.listView.KeyDown     += listView_KeyDown;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 폼 로드시 처리하기 - Form_Load(sender, e)

        /// <summary>
        /// 폼 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_Load(object sender, EventArgs e)
        {
            this.imageList.ImageSize = new Size(64, 64);

            this.listView.View           = View.Tile;
            this.listView.LargeImageList = this.imageList;
        }

        #endregion
        #region 열기 메뉴 항목 클릭시 처리하기 - openMenuItem_Click(sender, e)

        /// <summary>
        /// 열기 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void openMenuItem_Click(object sender, EventArgs e)
        {
            if(this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                foreach(string filePath in openFileDialog.FileNames)
                {
                    Bitmap bitmap = new Bitmap(filePath);

                    this.imageList.Images.Add(bitmap);

                    this.listView.Items.Add(filePath, this.imageList.Images.Count - 1);
                }

                this.saveAsMenuItem.Enabled = (this.listView.Items.Count > 0);
            }
        }

        #endregion
        #region 다른 이름으로 저장 메뉴 항목 클릭시 처리하기 - saveAsMenuItem_Click(sender, e)

        /// <summary>
        /// 다른 이름으로 저장 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void saveAsMenuItem_Click(object sender, EventArgs e)
        {
            if(this.saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                List<Bitmap> bitmapList = new List<Bitmap>();

                foreach(ListViewItem item in this.listView.Items)
                {
                    bitmapList.Add(new Bitmap(item.Text));
                }

                IconHelper.Save(bitmapList, this.saveFileDialog.FileName, true);

                Icon = new Icon(this.saveFileDialog.FileName);
            }
        }

        #endregion
        #region 종료 메뉴 항목 클릭시 처리하기 - exitMenuItem_Click(sender, e)

        /// <summary>
        /// 종료 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void exitMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        #endregion
        #region 지우기 메뉴 항목 클릭시 처리하기 - clearMenuItem_Click(sender, e)

        /// <summary>
        /// 지우기 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void clearMenuItem_Click(object sender, EventArgs e)
        {
            this.listView.Items.Clear();

            this.imageList.Images.Clear();

            this.saveAsMenuItem.Enabled = (this.listView.Items.Count > 0);
        }

        #endregion
        #region 리스트 뷰 키 DOWN 처리하기 - listView_KeyDown(sender, e)

        /// <summary>
        /// 리스트 뷰 키 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void listView_KeyDown(object sender, KeyEventArgs e)
        {
            if(e.KeyCode == Keys.Delete)
            {
                foreach(ListViewItem item in listView.SelectedItems)
                {
                    item.Remove();
                }

                if(this.listView.Items.Count == 0)
                {
                    this.saveAsMenuItem.Enabled = false;

                    this.imageList.Images.Clear();
                }
            }
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by 사용자 icodebroker

댓글을 달아 주세요