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

728x90
반응형
728x170

TestProject.zip
다운로드

▶ SignpostLabel.cs

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 푯말 레이블
    /// </summary>
    public class SignpostLabel : UserControl
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 컨테이너
        /// </summary>
        private Container container = null;

        /// <summary>
        /// 차분
        /// </summary>
        private int difference = 10;

        /// <summary>
        /// 외부 테두리 경로
        /// </summary>
        private GraphicsPath outerBorderPath = null;

        /// <summary>
        /// 내부 테두리 경로
        /// </summary>
        private GraphicsPath innerBorderPath = null;

        /// <summary>
        /// 하이라이트 경로
        /// </summary>
        private GraphicsPath highlightPath = null;

        /// <summary>
        /// 레이블 색상 1
        /// </summary>
        private Color labelColor1 = Color.Gray;

        /// <summary>
        /// 레이블 색상 펜 1
        /// </summary>
        private Pen labelColorPen1 = Pens.Gray;

        /// <summary>
        /// 레이블 색상 브러시 1
        /// </summary>
        private SolidBrush labelColorBrush1 = new SolidBrush(Color.Gray);

        /// <summary>
        /// 레이블 색상 2
        /// </summary>
        private Color labelColor2 = Color.Gray;

        /// <summary>
        /// 레이블 색상 펜 2
        /// </summary>
        private Pen labelColorPen2 = Pens.Gray;

        /// <summary>
        /// 레이블 색상 브러시 2
        /// </summary>
        private SolidBrush labelColorBrush2 = new SolidBrush(Color.FromArgb(127, Color.Gray));

        /// <summary>
        /// 하이라이트 색상
        /// </summary>
        private Color highlightColor = Color.White;

        /// <summary>
        /// 하이라이트 펜
        /// </summary>
        private Pen highlightPen = Pens.White;

        /// <summary>
        /// 배경 이미지
        /// </summary>
        private Image backgroundImage = null;

        /// <summary>
        /// 이미지 정렬
        /// </summary>
        private ContentAlignment imageAlignment = ContentAlignment.MiddleLeft;

        /// <summary>
        /// 이미지 크기
        /// </summary>
        private Size imageSize = new Size(24, 24);

        /// <summary>
        /// 이미지 사각형
        /// </summary>
        private Rectangle imageRectangle = new Rectangle(8, 8, 24, 24);

        /// <summary>
        /// 이미지
        /// </summary>
        private Image image = null;

        /// <summary>
        /// 제목 정렬
        /// </summary>
        private ContentAlignment titleAlignment = ContentAlignment.MiddleCenter;

        /// <summary>
        /// 제목 사각형
        /// </summary>
        private Rectangle titleRectangle;

        /// <summary>
        /// 제목 문자열 포맷
        /// </summary>
        private StringFormat textStringFormat = null;

        /// <summary>
        /// 제목 색상
        /// </summary>
        private Color titleColor = Color.White;

        /// <summary>
        /// 제목 브러시
        /// </summary>
        private SolidBrush titleBrush = new SolidBrush(Color.White);

        /// <summary>
        /// 제목
        /// </summary>
        private string title;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 차분 - Difference

        /// <summary>
        /// 차분
        /// </summary>
        [Category("SignpostLabel")]
        public int Difference
        {
            get
            {
                return this.difference;
            }
            set
            {
                this.difference = value;

                SetOuterBorderPath(ClientRectangle, this.difference);

                SetInnerBorderPath(ClientRectangle, this.difference);

                SetHighlightPath(ClientRectangle, this.difference);

                SetTitleRectangle(ClientRectangle, this.difference);

                Invalidate();
            }
        }

        #endregion
        #region 레이블 색상 1 - LabelColor1

        /// <summary>
        /// 레이블 색상 1
        /// </summary>
        [Category("SignpostLabel")]
        public Color LabelColor1
        {
            get
            {
                return this.labelColor1;
            }
            set
            {
                this.labelColor1 = value;

                this.labelColorPen1 = new Pen(this.labelColor1);

                this.labelColorBrush1 = new SolidBrush(this.labelColor1);
                
                Invalidate();
            }
        }

        #endregion
        #region 레이블 색상 2 - LabelColor2

        /// <summary>
        /// 레이블 색상 2
        /// </summary>
        [Category("SignpostLabel")]
        public Color LabelColor2
        {
            get
            {
                return this.labelColor2;
            }
            set
            {
                this.labelColor2 = value;
                
                this.labelColorPen2 = new Pen(this.labelColor2);

                this.labelColorBrush2 = new SolidBrush(Color.FromArgb(127, this.labelColor2));

                Invalidate();
            }
        }

        #endregion
        #region 하이라이트 색상 - HighlightColor

        /// <summary>
        /// 하이라이트 색상
        /// </summary>
        [Category("SignpostLabel")]
        public Color HighlightColor
        {
            get
            {
                return this.highlightColor;
            }
            set
            {
                this.highlightColor = value;

                this.highlightPen = new Pen(this.highlightColor);
                
                Invalidate();
            }
        }

        #endregion
        #region 배경 이미지 - BackgroundImage

        /// <summary>
        /// 배경 이미지
        /// </summary>
        [Category("SignpostLabel")]
        public new Image BackgroundImage
        {
            get
            {
                return this.backgroundImage;
            }
            set
            {
                this.backgroundImage = value;
                
                Invalidate();
            }
        }

        #endregion
        #region 이미지 정렬 - ImageAlignment

        /// <summary>
        /// 이미지 정렬
        /// </summary>
        [Category("SignpostLabel")]
        public ContentAlignment ImageAlignment
        {
            get
            {
                return this.imageAlignment;
            }
            set
            {
                this.imageAlignment = value;
                
                Invalidate();
            }
        }

        #endregion
        #region 이미지 크기 - ImageSize

        /// <summary>
        /// 이미지 크기
        /// </summary>
        [Category("SignpostLabel")]
        public Size ImageSize
        {
            get
            {
                return this.imageSize;
            }
            set
            {
                this.imageSize = value;

                SetImageRectangle(this.imageSize);
                
                Invalidate();
            }
        }

        #endregion
        #region 이미지 - Image

        /// <summary>
        /// 이미지
        /// </summary>
        [Category("SignpostLabel")]
        public Image Image
        {
            get
            {
                return this.image;
            }
            set
            {
                this.image = value;
                
                Invalidate();
            }
        }

        #endregion
        #region 제목 정렬 - TitleAlignment

        /// <summary>
        /// 제목 정렬
        /// </summary>
        [Category("SignpostLabel")]
        public ContentAlignment TitleAlignment
        {
            get
            {
                return this.titleAlignment;
            }
            set
            {
                this.titleAlignment = value;

                SetTitleStringFormat(this.titleAlignment);
                
                Invalidate();
            }
        }

        #endregion
        #region 제목 색상 - TitleColor

        /// <summary>
        /// 제목 색상
        /// </summary>
        [Category("SignpostLabel")]
        public Color TitleColor
        {
            get
            {
                return this.titleColor;
            }
            set
            {
                this.titleColor = value;

                this.titleBrush = new SolidBrush(this.titleColor);
                
                Invalidate();
            }
        }

        #endregion
        #region 제목 - Title

        /// <summary>
        /// 제목
        /// </summary>
        [Category("SignpostLabel")]
        public string Title
        {
            get
            {
                return this.title;
            }
            set
            {
                this.title = value;

                Invalidate();
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - SignpostLabel()

        /// <summary>
        /// 생성자
        /// </summary>
        public SignpostLabel()
        {
            Name      = "signpostLabel";
            Size      = new Size(100, 32);
            BackColor = SystemColors.Control;
            TabStop   = false;

            SetOuterBorderPath(ClientRectangle, this.difference);

            SetInnerBorderPath(ClientRectangle, this.difference);

            SetHighlightPath(ClientRectangle, this.difference);

            SetTitleStringFormat(this.titleAlignment);

            SetTitleRectangle(ClientRectangle, this.difference);

            this.SetStyle(ControlStyles.AllPaintingInWmPaint        , true);
            this.SetStyle(ControlStyles.DoubleBuffer                , true);
            this.SetStyle(ControlStyles.ResizeRedraw                , true);
            this.SetStyle(ControlStyles.Selectable                  , true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.SetStyle(ControlStyles.UserPaint                   , true);

            Resize += UserControl_Resize;
            Paint  += UserControl_Paint;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 리소스 해제하기 - Dispose(disposing)

        /// <summary>
        /// 리소스 해제하기
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if(disposing)
            {
                if(this.container != null)
                {
                    this.container.Dispose();

                    this.outerBorderPath?.Dispose();

                    this.innerBorderPath?.Dispose();

                    this.highlightPath?.Dispose();

                    this.labelColorPen1?.Dispose();

                    this.labelColorBrush1?.Dispose();

                    this.labelColorPen2?.Dispose();

                    this.labelColorBrush2?.Dispose();

                    this.highlightPen?.Dispose();
                }
            }

            base.Dispose(disposing);
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 사용자 컨트롤 크기 변경시 처리하기 - UserControl_Resize(sender, e)

        /// <summary>
        /// 사용자 컨트롤 크기 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void UserControl_Resize(object sender, EventArgs e)
        {
            Rectangle rectangle = ClientRectangle;

            rectangle.X -= 1;
            rectangle.Y -= 1;

            rectangle.Width  += 2;
            rectangle.Height += 2;

            using(GraphicsPath graphicsPath = GetLabelPath(rectangle, this.difference))
            {
                Region = new Region(graphicsPath);
            }

            SetOuterBorderPath(ClientRectangle, this.difference);

            SetInnerBorderPath(ClientRectangle, this.difference);

            SetHighlightPath(ClientRectangle, this.difference);

            SetTitleRectangle(ClientRectangle, this.difference);
        }

        #endregion
        #region 사용자 컨트롤 페인트시 처리하기 - UserControl_Paint(sender, e)

        /// <summary>
        /// 사용자 컨트롤 페인트시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void UserControl_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode     = SmoothingMode.AntiAlias;
            e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

            DrawBackgroundImage(e.Graphics);
            DrawHighlight(e.Graphics);
            DrawImage(e.Graphics);
            DrawText(e.Graphics);
            DrawInnerBorder(e.Graphics);
            DrawOuterBorder(e.Graphics);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 레이블 경로 구하기 - GetLabelPath(rectangle, difference)

        /// <summary>
        /// 레이블 경로 구하기
        /// </summary>
        /// <param name="rectangle">사각형</param>
        /// <param name="difference">차분</param>
        /// <returns>레이블 경로</returns>
        private GraphicsPath GetLabelPath(RectangleF rectangle, float difference)
        {
            float x1 = rectangle.X;
            float y1 = rectangle.Y;

            float x2 = rectangle.X + rectangle.Width - difference;
            float y2 = rectangle.Y;

            float x3 = rectangle.X + rectangle.Width;
            float y3 = rectangle.Y + rectangle.Height / 2;

            float x4 = rectangle.X + rectangle.Width - difference;
            float y4 = rectangle.Y + rectangle.Height;

            float x5 = rectangle.X;
            float y5 = rectangle.Y + rectangle.Height;

            GraphicsPath graphicsPath = new GraphicsPath();

            graphicsPath.AddLine(x1, y1, x2, y2);
            graphicsPath.AddLine(x2, y2, x3, y3);
            graphicsPath.AddLine(x3, y3, x4, y4);
            graphicsPath.AddLine(x4, y4, x5, y5);

            return graphicsPath;
        }

        #endregion
        #region 하이라이트 레이블 경로 구하기 - GetHighlightLabelPath(rectangle, difference)

        /// <summary>
        /// 하이라이트 레이블 경로 구하기
        /// </summary>
        /// <param name="rectangle">사각형</param>
        /// <param name="difference">차분</param>
        /// <returns>하이라이트 레이블 경로</returns>
        private GraphicsPath GetHighlightLabelPath(RectangleF rectangle, float difference)
        {
            float x1 = rectangle.X;
            float y1 = rectangle.Y;

            float x2 = rectangle.X + rectangle.Width - difference;
            float y2 = rectangle.Y;

            float x3 = rectangle.X + rectangle.Width;
            float y3 = rectangle.Y + rectangle.Height / 2;

            float x4 = rectangle.X;
            float y4 = rectangle.Y + rectangle.Height / 2;

            GraphicsPath graphicsPath = new GraphicsPath();

            graphicsPath.AddLine(x1, y1, x2, y2);
            graphicsPath.AddLine(x2, y2, x3, y3);
            graphicsPath.AddLine(x3, y3, x4, y4);

            return graphicsPath;
        }

        #endregion

        #region 하이라이트 경로 설정하기 - SetHighlightPath(clientRectangle, difference)

        /// <summary>
        /// 하이라이트 경로 설정하기
        /// </summary>
        /// <param name="clientRectangle">클라이언트 사각형</param>
        /// <param name="difference">차분</param>
        private void SetHighlightPath(Rectangle clientRectangle, int difference)
        {
            this.highlightPath?.Dispose();

            this.highlightPath = GetHighlightLabelPath(clientRectangle, difference);
        }

        #endregion
        #region 이미지 사각형 설정하기 - SetImageRectangle(imageSize)

        /// <summary>
        /// 이미지 사각형 설정하기
        /// </summary>
        /// <param name="imageSize">이미지 크기</param>
        private void SetImageRectangle(Size imageSize)
        {
            Rectangle imageRectangle = new Rectangle(8, 8, imageSize.Width, imageSize.Height);

            switch(this.imageAlignment)
            {
                case ContentAlignment.TopCenter :

                    imageRectangle = new Rectangle
                    (
                        Width / 2 - imageSize.Width / 2,
                        8,
                        imageSize.Width,
                        imageSize.Height
                    );

                    break;

                case ContentAlignment.TopRight :

                    imageRectangle = new Rectangle
                    (
                        Width - 8 - imageSize.Width,
                        8,
                        imageSize.Width,
                        imageSize.Height
                    );

                    break;

                case ContentAlignment.MiddleLeft :

                    imageRectangle = new Rectangle
                    (
                        8,
                        Height / 2 - imageSize.Height / 2,
                        imageSize.Width,
                        imageSize.Height
                    );

                    break;

                case ContentAlignment.MiddleCenter :

                    imageRectangle = new Rectangle
                    (
                        Width  / 2 - imageSize.Width  / 2,
                        Height / 2 - imageSize.Height / 2,
                        imageSize.Width,
                        imageSize.Height
                    );

                    break;

                case ContentAlignment.MiddleRight :

                    imageRectangle = new Rectangle
                    (
                        Width  - 8 - imageSize.Width,
                        Height / 2 - imageSize.Height / 2,
                        imageSize.Width,
                        imageSize.Height
                    );

                    break;

                case ContentAlignment.BottomLeft :

                    imageRectangle = new Rectangle
                    (
                        8,
                        Height - 8 - imageSize.Height,
                        imageSize.Width,
                        imageSize.Height
                    );

                    break;

                case ContentAlignment.BottomCenter :

                    imageRectangle = new Rectangle
                    (
                        Width  / 2 - imageSize.Width / 2,
                        Height - 8 - imageSize.Height,
                        imageSize.Width,
                        imageSize.Height
                    );

                    break;

                case ContentAlignment.BottomRight :

                    imageRectangle = new Rectangle
                    (
                        Width  - 8 - imageSize.Width,
                        Height - 8 - imageSize.Height,
                        imageSize.Width,
                        imageSize.Height
                    );

                    break;
            }

            this.imageRectangle = imageRectangle;
        }

        #endregion
        #region 제목 사각형 설정하기 - SetTitleRectangle(clientRectangle, difference)

        /// <summary>
        /// 제목 사각형 설정하기
        /// </summary>
        /// <param name="clientRectangle">클라이언트 사각형</param>
        /// <param name="difference">차분</param>
        private void SetTitleRectangle(Rectangle clientRectangle, int difference)
        {
            this.titleRectangle = new Rectangle(8, 4, clientRectangle.Width - difference - 8, clientRectangle.Height - 4);
        }

        #endregion
        #region 제목 문자열 포맷 설정하기 - SetTitleStringFormat(titleAlignment)

        /// <summary>
        /// 제목 문자열 포맷 설정하기
        /// </summary>
        /// <param name="titleAlignment">제목 정렬</param>
        private void SetTitleStringFormat(ContentAlignment titleAlignment)
        {
            StringFormat stringFormat = new StringFormat();

            stringFormat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.LineLimit;

            switch(titleAlignment)
            {
                case ContentAlignment.TopLeft   :
                case ContentAlignment.TopCenter :
                case ContentAlignment.TopRight  :

                    stringFormat.LineAlignment = StringAlignment.Near;

                    break;

                case ContentAlignment.MiddleLeft   :
                case ContentAlignment.MiddleCenter :
                case ContentAlignment.MiddleRight  :

                    stringFormat.LineAlignment = StringAlignment.Center;

                    break;

                case ContentAlignment.BottomLeft   :
                case ContentAlignment.BottomCenter :
                case ContentAlignment.BottomRight  :

                    stringFormat.LineAlignment = StringAlignment.Far;
                    break;
            }

            switch(titleAlignment)
            {
                case ContentAlignment.TopLeft    :
                case ContentAlignment.MiddleLeft :
                case ContentAlignment.BottomLeft :

                    stringFormat.Alignment = StringAlignment.Near;

                    break;

                case ContentAlignment.TopCenter    :
                case ContentAlignment.MiddleCenter :
                case ContentAlignment.BottomCenter :

                    stringFormat.Alignment = StringAlignment.Center;

                    break;

                case ContentAlignment.TopRight    :
                case ContentAlignment.MiddleRight :
                case ContentAlignment.BottomRight :

                    stringFormat.Alignment = StringAlignment.Far;

                    break;
            }

            this.textStringFormat = stringFormat;
        }

        #endregion
        #region 내부 테두리 경로 설정하기 - SetInnerBorderPath(clientRectangle, difference)

        /// <summary>
        /// 내부 테두리 경로 설정하기
        /// </summary>
        /// <param name="clientRectangle">클라이언트 사각형</param>
        /// <param name="difference">차분</param>
        private void SetInnerBorderPath(Rectangle clientRectangle, int difference)
        {
            Rectangle rectangle = clientRectangle;

            rectangle.X++;
            rectangle.Y++;

            rectangle.Width  -= 3;
            rectangle.Height -= 3;

            GraphicsPath innerBorderPath = GetLabelPath(rectangle, difference);

            this.innerBorderPath?.Dispose();

            this.innerBorderPath = innerBorderPath;
        }

        #endregion
        #region 외부 테두리 경로 설정하기 - SetOuterBorderPath(clientRectangle, difference)

        /// <summary>
        /// 외부 테두리 경로 설정하기
        /// </summary>
        /// <param name="clientRectangle">클라이언트 사각형</param>
        /// <param name="difference">차분</param>
        private void SetOuterBorderPath(Rectangle clientRectangle, int difference)
        {
            Rectangle rectangle = clientRectangle;

            rectangle.Width--;
            rectangle.Height--;

            GraphicsPath outerBorderPath = GetLabelPath(rectangle, difference);

            this.outerBorderPath?.Dispose();

            this.outerBorderPath = outerBorderPath;
        }

        #endregion

        #region 배경 이미지 그리기 - DrawBackgroundImage(graphics)

        /// <summary>
        /// 배경 이미지 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawBackgroundImage(Graphics graphics)
        {
            graphics.FillPath(this.labelColorBrush1, this.outerBorderPath);

            graphics.SetClip(this.innerBorderPath);

            if(this.backgroundImage != null)
            {
                graphics.DrawImage(this.backgroundImage, ClientRectangle);
            }

            graphics.ResetClip();

            graphics.FillPath(this.labelColorBrush2, this.outerBorderPath);
        }

        #endregion
        #region 하이라이트 그리기 - DrawHighlight(graphics)

        /// <summary>
        /// 하이라이트 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawHighlight(Graphics graphics)
        {
            if(!Enabled)
            {
                return;
            }

            LinearGradientBrush linearGradientBrush = new LinearGradientBrush
            (
                this.highlightPath.GetBounds(),
                Color.FromArgb(150, this.HighlightColor),
                Color.FromArgb(50 , this.HighlightColor),
                LinearGradientMode.Vertical
            );

            graphics.FillPath(linearGradientBrush, this.highlightPath);

            linearGradientBrush.Dispose();
        }

        #endregion
        #region 이미지 그리기 - DrawImage(graphics)

        /// <summary>
        /// 이미지 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawImage(Graphics graphics)
        {
            if(this.image == null)
            {
                return;
            }

            graphics.DrawImage(this.image, this.imageRectangle);
        }

        #endregion
        #region 텍스트 그리기 - DrawText(graphics)

        /// <summary>
        /// 텍스트 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawText(Graphics graphics)
        {
            graphics.DrawString
            (
                this.title,
                Font,
                this.titleBrush,
                this.titleRectangle,
                this.textStringFormat
            );
        }

        #endregion
        #region 내부 테두리 그리기 - DrawInnerBorder(graphics)

        /// <summary>
        /// 내부 테두리 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawInnerBorder(Graphics graphics)
        {
            graphics.DrawPath(this.highlightPen, this.innerBorderPath);
        }

        #endregion
        #region 외부 테두리 그리기 - DrawOuterBorder(graphics)

        /// <summary>
        /// 외부 테두리 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawOuterBorder(Graphics graphics)
        {
            graphics.DrawPath(this.labelColorPen2, this.outerBorderPath);
        }

        #endregion
    }
}
728x90
반응형
그리드형
Posted by 사용자 icodebroker

댓글을 달아 주세요