첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

■ GroupBox 클래스 : 익스팬더(Expander) 그룹 박스 사용하기

------------------------------------------------------------------------------------------------------------------------


TestProject.zip


GraphicsHelper.cs

 

 

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

 

namespace TestProject

{

    /// <SUMMARY>

    /// 그래픽스 헬퍼

    /// </SUMMARY>

    public class GraphicsHelper

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

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

 

        #region Field

 

        /// <summary>

        /// 그래픽스

        /// </summary>

        private Graphics graphics;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 그래픽스 - Graphics

 

        /// <summary>

        /// 그래픽스

        /// </summary>

        public Graphics Graphics

        {

            get

            {

                return this.graphics;

            }

            set

            {

                this.graphics = value;

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - GraphicsHelper(graphics)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        public GraphicsHelper(Graphics graphics)

        {

            this.graphics = graphics;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 캡슐 경로 구하기 - GetCapsulePath(rectangle)

 

        /// <summary>

        /// 캡슐 경로 구하기

        /// </summary>

        /// <param name="rectangle">사각형</param>

        /// <returns>캡슐 경로</returns>

        private GraphicsPath GetCapsulePath(RectangleF rectangle)

        {

            float diameter;

 

            RectangleF arcRectangle;

 

            GraphicsPath path = new GraphicsPath();

 

            try

            {

                if(rectangle.Width > rectangle.Height)

                {

                    diameter = rectangle.Height;

 

                    SizeF size = new SizeF(diameter, diameter);

 

                    arcRectangle = new RectangleF(rectangle.Location, size);

 

                    path.AddArc(arcRectangle, 90, 180);

 

                    arcRectangle.X = rectangle.Right-diameter;

 

                    path.AddArc(arcRectangle, 270, 180);

                }

                else if(rectangle.Width < rectangle.Height)

                {

                    diameter = rectangle.Width;

 

                    SizeF size = new SizeF(diameter, diameter);

 

                    arcRectangle = new RectangleF(rectangle.Location, size);

 

                    path.AddArc(arcRectangle, 180, 180);

 

                    arcRectangle.Y = rectangle.Bottom-diameter;

 

                    path.AddArc(arcRectangle, 0, 180);

                }

                else

                {

                    path.AddEllipse(rectangle);

                }

            }

            catch(Exception)

            {

                path.AddEllipse(rectangle);

            }

            finally

            {

                path.CloseFigure();

            }

 

            return path;

        }

 

        #endregion

        #region 라운드 사각형 경로 구하기 - GetRoundRectanglePath(rectangle, radius)

 

        /// <summary>

        /// 라운드 사각형 경로 구하기

        /// </summary>

        /// <param name="rectangle">사각형</param>

        /// <param name="radius">반지름</param>

        /// <returns>라운드 사각형 경로</returns>

        private GraphicsPath GetRoundRectanglePath(RectangleF rectangle, float radius)

        {

            if(radius <= 0.0f)

            {

                GraphicsPath graphicsPath = new GraphicsPath();

 

                graphicsPath.AddRectangle(rectangle);

 

                graphicsPath.CloseFigure();

 

                return graphicsPath;

            }

 

            if(radius >= (Math.Min(rectangle.Width, rectangle.Height)) / 2.0)

            {

                return GetCapsulePath(rectangle);

            }

 

            float diameter = radius * 2.0f;

 

            SizeF size = new SizeF(diameter, diameter);

 

            RectangleF arcRectangle = new RectangleF(rectangle.Location, size);

 

            GraphicsPath path = new GraphicsPath();

 

            path.AddArc(arcRectangle, 180, 90);

 

            arcRectangle.X = rectangle.Right-diameter;

 

            path.AddArc(arcRectangle, 270, 90);

 

            arcRectangle.Y = rectangle.Bottom-diameter;

 

            path.AddArc(arcRectangle, 0, 90);

 

            arcRectangle.X = rectangle.Left;

 

            path.AddArc(arcRectangle, 90, 90);

 

            path.CloseFigure();

 

            return path;

        }

 

        #endregion

        #region 라운드 사각형 채우기 - FillRoundRectangle(brush, x, y, width, height, radius)

 

        /// <summary>

        /// 라운드 사각형 채우기

        /// </summary>

        /// <param name="brush">브러시</param>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="width">너비</param>

        /// <param name="height">높이</param>

        /// <param name="radius">반지름</param>

        public void FillRoundRectangle(Brush brush, int x, int y, int width, int height, int radius)

        {

            float xFloat      = Convert.ToSingle(x);

            float yFloat      = Convert.ToSingle(y);

            float widthFloat  = Convert.ToSingle(width);

            float heightFloat = Convert.ToSingle(height);

            float radiusFloat = Convert.ToSingle(radius);

 

            FillRoundRectangle(brush, xFloat, yFloat, widthFloat, heightFloat, radiusFloat);

        }

 

        #endregion

        #region 라운드 사각형 채우기 - FillRoundRectangle(brush, x, y, width, height, radius)

 

        /// <summary>

        /// 라운드 사각형 채우기

        /// </summary>

        /// <param name="brush">브러시</param>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="width">너비</param>

        /// <param name="height">높이</param>

        /// <param name="radius">반지름</param>

        public void FillRoundRectangle(Brush brush, float x, float y, float width, float height, float radius)

        {

            RectangleF rectangle = new RectangleF(x, y, width, height);

 

            GraphicsPath graphicsPath = GetRoundRectanglePath(rectangle, radius);

 

            this.graphics.FillPath(brush, graphicsPath);

        }

 

        #endregion

        #region 라운드 사각형 그리기 - DrawRoundRectangle(pen, x, y, width, height, radius)

 

        /// <summary>

        /// 라운드 사각형 그리기

        /// </summary>

        /// <param name="pen"></param>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="width">너비</param>

        /// <param name="height">높이</param>

        /// <param name="radius">반지름</param>

        public void DrawRoundRectangle(Pen pen, float x, float y, float width, float height, float radius)

        {

            RectangleF rectangle = new RectangleF(x, y, width, height);

 

            GraphicsPath graphicsPath = GetRoundRectanglePath(rectangle, radius);

 

            this.graphics.DrawPath(pen, graphicsPath);

        }

 

        #endregion

        #region 라운드 사각형 그리기 - DrawRoundRectangle(pen, x, y, width, height, radius)

 

        /// <summary>

        /// 라운드 사각형 그리기

        /// </summary>

        /// <param name="pen"></param>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="width">너비</param>

        /// <param name="height">높이</param>

        /// <param name="radius">반지름</param>

        public void DrawRoundRectangle(Pen pen, int x, int y, int width, int height, int radius)

        {

            float xFloat      = Convert.ToSingle(x);

            float yFloat      = Convert.ToSingle(y);

            float widthFloat  = Convert.ToSingle(width);

            float heightFloat = Convert.ToSingle(height);

            float radiusFloat = Convert.ToSingle(radius);

 

            DrawRoundRectangle(pen, xFloat, yFloat, widthFloat, heightFloat, radiusFloat);

        }

 

        #endregion

    }

}

 

 

Expander.cs

 

 

using System;

using System.ComponentModel;

using System.Drawing;

using System.Reflection;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 익스팬더

    /// </summary>

    [ToolboxBitmap(typeof(Expander))]

    public class Expander : GroupBox

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Delegate

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 익스팬더 버튼 클릭 이벤트 핸들러 - ExpanderButtonClickEventHandler(sender, e)

 

        /// <summary>

        /// 익스팬더 버튼 클릭 이벤트 핸들러

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        public delegate void ExpanderButtonClickEventHandler(object sender, EventArgs e);

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Event

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 익스팬더 버튼 클릭 이벤트 - ExpanderButtonClick

 

        /// <summary>

        /// 익스팬더 버튼 클릭 이벤트

        /// </summary>

        public event ExpanderButtonClickEventHandler ExpanderButtonClick;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

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

 

        #region Field

 

        /// <summary>

        /// 토글 사각형

        /// </summary>

        private Rectangle toggleRectangle = new Rectangle(8, 2, 11, 11);

 

        /// <summary>

        /// 테두리 색상

        /// </summary>

        private Color borderColor = Color.FromArgb(208, 208, 191);

 

        /// <summary>

        /// 텍스트 색상

        /// </summary>

        private Color textColor = Color.FromArgb(0, 70, 213);

 

        /// <summary>

        /// 축소 여부

        /// </summary>

        private bool isCollapsed = false;

 

        /// <summary>

        /// 크기 조정 여부

        /// </summary>

        private bool isResizing = false;

 

        /// <summary>

        /// 축소시 높이

        /// </summary>

        private const int COLLAPSED_HEIGHT = 20;

 

        /// <summary>

        /// 완전한 크기

        /// </summary>

        private Size fullSize = Size.Empty;

 

        /// <summary>

        /// 컨테이너

        /// </summary>

        private Container container = null;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 완전한 높이 - FullHeight

 

        /// <summary>

        /// 완전한 높이

        /// </summary>

        [Browsable(false)]

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

        public int FullHeight

        {

            get

            {

                return this.fullSize.Height;

            }

        }

 

        #endregion

        #region 축소시 높이 - CollapsedHeight

 

        /// <summary>

        /// 축소시 높이

        /// </summary>

        [Browsable(false)]

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

        public int CollapsedHeight

        {

            get

            {

                return COLLAPSED_HEIGHT;

            }

        }

 

        #endregion

        #region 축소 여부 - IsCollapsed

 

        /// <summary>

        /// 축소 여부

        /// </summary>

        [Browsable(false)]

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

        [DefaultValue(false)]

        public bool IsCollapsed

        {

            get

            {

                return this.isCollapsed;

            }

            set

            {

                if(value != this.isCollapsed)

                {

                    this.isCollapsed = value;

 

                    if(!value)

                    {

                        Size = this.fullSize;

                    }

                    else

                    {

                        this.isResizing = true;

 

                        Height = COLLAPSED_HEIGHT;

 

                        this.isResizing = false;

                    }

 

                    foreach(Control control in Controls)

                    {

                        control.Visible = !value;

                    }

 

                    Invalidate();

                }

            }

        }

 

        #endregion

 

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

 

        #region 마이너스 이미지 - MinusImage

 

        /// <summary>

        /// 마이너스 이미지

        /// </summary>

        private Image MinusImage

        {

            get

            {

                return Image.FromStream

                (

                    Assembly.GetExecutingAssembly().GetManifestResourceStream("TestProject.RESOURCE.minus.bmp")

                );

            }

        }

 

        #endregion

        #region 플러스 이미지 - PlusImage

 

        /// <summary>

        /// 플러스 이미지

        /// </summary>

        private Image PlusImage

        {

            get

            {

                return Image.FromStream

                (

                    Assembly.GetExecutingAssembly().GetManifestResourceStream("TestProject.RESOURCE.plus.bmp")

                );

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - Expander()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public Expander()

        {

            InitializeComponent();

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

 

        #region 마우스 UP 처리하기 - OnMouseUp(e)

 

        /// <summary>

        /// 마우스 UP 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected override void OnMouseUp(MouseEventArgs e)

        {

            if(this.toggleRectangle.Contains(new Point(e.X, e.Y)))

            {

                Toggle();

            }

            else

            {

                base.OnMouseUp(e);

            }

        }

 

        #endregion

        #region 페인트시 처리하기 - OnPaint(e)

 

        /// <summary>

        /// 페인트시 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected override void OnPaint(PaintEventArgs e)

        {

            SetFullSize();

 

            DrawGroupBox(e.Graphics);

 

            DrawExpanderButton(e.Graphics);

        }

 

        #endregion

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

 

        /// <summary>

        /// 리소스 해제하기

        /// </summary>

        /// <param name="disposing">리소스 해제 여부</param>

        protected override void Dispose(bool disposing)

        {

            if(disposing)

            {

                if(container != null)

                {

                    container.Dispose();

                }

            }

 

            base.Dispose(disposing);

        }

 

        #endregion

 

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

 

        #region 컴포넌트 초기화하기 - InitializeComponent()

 

        /// <summary>

        /// 컴포넌트 초기화하기

        /// </summary>

        private void InitializeComponent()

        {

            this.container = new Container();

        }

 

        #endregion

        #region 그룹 박스 그리기 - DrawGroupBox(graphics)

 

        /// <summary>

        /// 그룹 박스 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        private void DrawGroupBox(Graphics graphics)

        {

            Rectangle boundRectangle = new Rectangle

            (

                ClientRectangle.X,

                ClientRectangle.Y + 6,

                ClientRectangle.Width,

                ClientRectangle.Height - 6

            );

 

            int textLeft  = (boundRectangle.X + 8) + this.toggleRectangle.Width + 2;

            int textWidth = (int)graphics.MeasureString(Text, Font).Width;

 

            textWidth = textWidth < 1 ? 1 : textWidth;

 

            int textRight = textLeft + textWidth + 1;

 

            GraphicsHelper helper = new GraphicsHelper(graphics);

 

            using(Pen pen = new Pen(this.borderColor))

            {

                helper.DrawRoundRectangle(pen, 0, 6, Width - 1, Height - 7, 5f);

            }

 

            graphics.FillRectangle

            (

                SystemBrushes.Control,

                8,

                2,

                textRight - 8,

                12

            );

 

            using(SolidBrush brush = new SolidBrush(this.textColor))

            {

                graphics.DrawString(Text, this.Font, brush, textLeft, 0);

            }

        }

 

        #endregion

        #region 익스팬더 버튼 그리기 - DrawExpanderButton(graphics)

 

        /// <summary>

        /// 익스팬더 버튼 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        private void DrawExpanderButton(Graphics graphics)

        {

            if(IsCollapsed)

            {

                graphics.DrawImage(PlusImage, this.toggleRectangle);

            }

            else

            {

                graphics.DrawImage(MinusImage, this.toggleRectangle);

            }

        }

 

        #endregion

        #region 토글하기 - Toggle()

 

        /// <summary>

        /// 토글하기

        /// </summary>

        private void Toggle()

        {

            IsCollapsed = !IsCollapsed;

 

            ExpanderButtonClick?.Invoke(this, EventArgs.Empty);

        }

 

        #endregion

        #region 완전한 크기 설정하기 - SetFullSize()

 

        /// <summary>

        /// 완전한 크기 설정하기

        /// </summary>

        private void SetFullSize()

        {

            if(!this.isResizing && !this.isCollapsed)

            {

                this.fullSize = Size;

            }

        }

 

        #endregion

    }

}

 

------------------------------------------------------------------------------------------------------------------------

Posted by 사용자 icodebroker
TAG

댓글을 달아 주세요