728x90
반응형
728x170
■ GroupBox 클래스를 사용해 익스팬더(Expander) 그룹 박스를 만드는 방법을 보여준다.
▶ 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
}
}
728x90
반응형
그리드형(광고전용)
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] Form 클래스 : CreateParams 속성을 사용해 포커스 설정 방지하기 (0) | 2020.02.12 |
---|---|
[C#/WINFORM] 멀티 원격 데스크톱 프로토콜 클라이언트(Remote Desktop Protocol Client) 사용하기 (0) | 2020.02.10 |
[C#/WINFORM] GIF 에디터 사용하기 (0) | 2020.01.28 |
[C#/WINFORM] Control 클래스 : MousePosition 정적 속성을 사용해 마우스 위치 색상 구하기 (0) | 2020.01.26 |
[C#/WINFORM] Cursor 클래스 : Clip 정적 속성을 사용해 마우스 이동 영역 제한하기 (0) | 2020.01.13 |
[C#/WINFORM] ScrollableControl 클래스 : 접을 수 있는 패널 사용하기 (0) | 2020.01.05 |
[C#/WINFORM] UserControl 클래스 : 푯말 레이블 사용하기 (0) | 2019.12.28 |
[C#/WINFORM] UserControl 클래스 : 체크 버튼 사용하기 (0) | 2019.12.28 |
[C#/WINFORM] Label 클래스 : 외곽선 텍스트 사용하기 (0) | 2019.12.27 |
[C#/WINFORM] WebBrowser 클래스 : DocumentText 속성을 사용해 HTML 문자열 설정하기 (0) | 2019.12.26 |
댓글을 달아 주세요