위와 같이 사각형을 늘리고 자유롭게 움직이는 간단한 프로젝트를 실시해보자.
일단 C# winform으로 프로젝트를 생성하자.
그런다음 하나의 Picturebox를 Form에 넣어주자.
Form의 코드는 다음과 같다.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Button;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
UserRect rect;
public Form1()
{
InitializeComponent();
rect = new UserRect(new Rectangle(10, 10, 100, 100));
rect.SetPictureBox(this.pictureBox1);
}
}
}
그런다음 UserRect이라는 프로젝트를 생성해준다.
UserRect의 중요한 함수들을 확인해보자.
사각형의 클릭포지션을 아래와같이 8가지로 나눠놓은것이다.
private enum PosSizableRect
{
UpMiddle,
LeftMiddle,
LeftBottom,
LeftUp,
RightUp,
RightMiddle,
RightBottom,
BottomMiddle,
None
};
해당 함수는 UserRect의 생성자이다. Rectangle을 사용하여 객체를 초기화한다.
public UserRect(Rectangle r)
{
rect = r;
mIsClick = false;
}
해당함수는 Graphics 개체를 사용하여 UserRect을 그리고 색깔은 빨간색이다. 또한 위에 Enum.GetValues을 이용하여 PosSizableRect 열거된 항목에 대한 사각형을 그린다.
public void Draw(Graphics g)
{
g.DrawRectangle(new Pen(Color.Red), rect);
foreach (PosSizableRect pos in Enum.GetValues(typeof(PosSizableRect)))
{
g.DrawRectangle(new Pen(Color.Red), GetRect(pos));
}
}
PictureBox의 MouseDown 이벤트 핸들러이다. 마우스 클릭 이벤트가 발생하면 호출된다. 클릭된 위치에 대한 정보를 바탕으로 nodeSelected 값에 설정하고 rect의 이용을 제어한다.
private void mPictureBox_MouseDown(object sender, MouseEventArgs e)
{
mIsClick = true;
nodeSelected = PosSizableRect.None;
nodeSelected = GetNodeSelectable(e.Location);
if (rect.Contains(new Point(e.X, e.Y)))
{
mMove = true;
}
oldX = e.X;
oldY = e.Y;
}
PictureBox의 MouseMove 이벤트 핸들러이다. 마우스 이동 이벤트가 발생시 호출되고 이동된 위치에 따라 nodeSelected 값을 기반으로 rect을 조작한다.
private void mPictureBox_MouseMove(object sender, MouseEventArgs e)
{
ChangeCursor(e.Location);
if (mIsClick == false)
{
return;
}
Rectangle backupRect = rect;
switch (nodeSelected)
{
case PosSizableRect.LeftUp:
rect.X += e.X - oldX;
rect.Width -= e.X - oldX;
rect.Y += e.Y - oldY;
rect.Height -= e.Y - oldY;
break;
case PosSizableRect.LeftMiddle:
rect.X += e.X - oldX;
rect.Width -= e.X - oldX;
break;
case PosSizableRect.LeftBottom:
rect.Width -= e.X - oldX;
rect.X += e.X - oldX;
rect.Height += e.Y - oldY;
break;
case PosSizableRect.BottomMiddle:
rect.Height += e.Y - oldY;
break;
case PosSizableRect.RightUp:
rect.Width += e.X - oldX;
rect.Y += e.Y - oldY;
rect.Height -= e.Y - oldY;
break;
case PosSizableRect.RightBottom:
rect.Width += e.X - oldX;
rect.Height += e.Y - oldY;
break;
case PosSizableRect.RightMiddle:
rect.Width += e.X - oldX;
break;
case PosSizableRect.UpMiddle:
rect.Y += e.Y - oldY;
rect.Height -= e.Y - oldY;
break;
default:
if (mMove)
{
rect.X = rect.X + e.X - oldX;
rect.Y = rect.Y + e.Y - oldY;
}
break;
}
oldX = e.X;
oldY = e.Y;
if (rect.Width < 5 || rect.Height < 5)
{
rect = backupRect;
}
TestIfRectInsideArea();
mPictureBox.Invalidate();
}
rect가 mPictureBox의 영역내에 있는지 확인한다. 만약 rect가 영역을 벗어난다면 영역 내에 유지되도록 조정한다.
private void TestIfRectInsideArea()
{
// Test if rectangle still inside the area.
if (rect.X < 0) rect.X = 0;
if (rect.Y < 0) rect.Y = 0;
if (rect.Width <= 0) rect.Width = 1;
if (rect.Height <= 0) rect.Height = 1;
if (rect.X + rect.Width > mPictureBox.Width)
{
rect.Width = mPictureBox.Width - rect.X - 1; // -1 to be still show
if (allowDeformingDuringMovement == false)
{
mIsClick = false;
}
}
if (rect.Y + rect.Height > mPictureBox.Height)
{
rect.Height = mPictureBox.Height - rect.Y - 1;// -1 to be still show
if (allowDeformingDuringMovement == false)
{
mIsClick = false;
}
}
}
PosSizableRect 열거형 값에 해당하는 크기 조절 가능한 사각형을 반환한다. 각 항목에 대해 CreateRectSizableNode함수를 호출하여 해당하는 사각형을 생성하고 반환한다,
private Rectangle GetRect(PosSizableRect p)
{
switch (p)
{
case PosSizableRect.LeftUp:
return CreateRectSizableNode(rect.X, rect.Y);
case PosSizableRect.LeftMiddle:
return CreateRectSizableNode(rect.X, rect.Y + +rect.Height / 2);
case PosSizableRect.LeftBottom:
return CreateRectSizableNode(rect.X, rect.Y + rect.Height);
case PosSizableRect.BottomMiddle:
return CreateRectSizableNode(rect.X + rect.Width / 2, rect.Y + rect.Height);
case PosSizableRect.RightUp:
return CreateRectSizableNode(rect.X + rect.Width, rect.Y);
case PosSizableRect.RightBottom:
return CreateRectSizableNode(rect.X + rect.Width, rect.Y + rect.Height);
case PosSizableRect.RightMiddle:
return CreateRectSizableNode(rect.X + rect.Width, rect.Y + rect.Height / 2);
case PosSizableRect.UpMiddle:
return CreateRectSizableNode(rect.X + rect.Width / 2, rect.Y);
default:
return new Rectangle();
}
}
주어진 좌표에 해당하는 크기조절 가능한 사각형 핸들을 반환한다. Enum.GetValues을 사용하여 PosSizableRect 열거형의 모든 항목을 반복하고, 주어진 좌표가 해당 사각형 내에 있는지 확인하여 해당하는 항목을 반환한다. 만약 주어진 좌표에 해당하는 사각형이 없는경우 None을 반환.
private PosSizableRect GetNodeSelectable(Point p)
{
foreach (PosSizableRect r in Enum.GetValues(typeof(PosSizableRect)))
{
if (GetRect(r).Contains(p))
{
return r;
}
}
return PosSizableRect.None;
}
크기 조절 가능한 사각형 핸들에 대한 커서를 반환한다. 각 항목에 대해 해당하는 커서를 반환.
private Cursor GetCursor(PosSizableRect p)
{
switch (p)
{
case PosSizableRect.LeftUp:
return Cursors.SizeNWSE;
case PosSizableRect.LeftMiddle:
return Cursors.SizeWE;
case PosSizableRect.LeftBottom:
return Cursors.SizeNESW;
case PosSizableRect.BottomMiddle:
return Cursors.SizeNS;
case PosSizableRect.RightUp:
return Cursors.SizeNESW;
case PosSizableRect.RightBottom:
return Cursors.SizeNWSE;
case PosSizableRect.RightMiddle:
return Cursors.SizeWE;
case PosSizableRect.UpMiddle:
return Cursors.SizeNS;
default:
return Cursors.Default;
}
}
UserRect 전체 코드
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public class UserRect
{
private PictureBox mPictureBox;
public Rectangle rect;
public bool allowDeformingDuringMovement = false;
private bool mIsClick = false;
private bool mMove = false;
private int oldX;
private int oldY;
private int sizeNodeRect = 5;
private Bitmap mBmp = null;
private PosSizableRect nodeSelected = PosSizableRect.None;
private int angle = 30;
private enum PosSizableRect
{
UpMiddle,
LeftMiddle,
LeftBottom,
LeftUp,
RightUp,
RightMiddle,
RightBottom,
BottomMiddle,
None
};
public UserRect(Rectangle r)
{
rect = r;
mIsClick = false;
}
public void Draw(Graphics g)
{
g.DrawRectangle(new Pen(Color.Red), rect);
foreach (PosSizableRect pos in Enum.GetValues(typeof(PosSizableRect)))
{
g.DrawRectangle(new Pen(Color.Red), GetRect(pos));
}
}
public void SetBitmapFile(string filename)
{
this.mBmp = new Bitmap(filename);
}
public void SetBitmap(Bitmap bmp)
{
this.mBmp = bmp;
}
public void SetPictureBox(PictureBox p)
{
this.mPictureBox = p;
mPictureBox.MouseDown += new MouseEventHandler(mPictureBox_MouseDown);
mPictureBox.MouseUp += new MouseEventHandler(mPictureBox_MouseUp);
mPictureBox.MouseMove += new MouseEventHandler(mPictureBox_MouseMove);
mPictureBox.Paint += new PaintEventHandler(mPictureBox_Paint);
}
private void mPictureBox_Paint(object sender, PaintEventArgs e)
{
try
{
Draw(e.Graphics);
}
catch (Exception exp)
{
System.Console.WriteLine(exp.Message);
}
}
private void mPictureBox_MouseDown(object sender, MouseEventArgs e)
{
mIsClick = true;
nodeSelected = PosSizableRect.None;
nodeSelected = GetNodeSelectable(e.Location);
if (rect.Contains(new Point(e.X, e.Y)))
{
mMove = true;
}
oldX = e.X;
oldY = e.Y;
}
private void mPictureBox_MouseUp(object sender, MouseEventArgs e)
{
mIsClick = false;
mMove = false;
}
private void mPictureBox_MouseMove(object sender, MouseEventArgs e)
{
ChangeCursor(e.Location);
if (mIsClick == false)
{
return;
}
Rectangle backupRect = rect;
switch (nodeSelected)
{
case PosSizableRect.LeftUp:
rect.X += e.X - oldX;
rect.Width -= e.X - oldX;
rect.Y += e.Y - oldY;
rect.Height -= e.Y - oldY;
break;
case PosSizableRect.LeftMiddle:
rect.X += e.X - oldX;
rect.Width -= e.X - oldX;
break;
case PosSizableRect.LeftBottom:
rect.Width -= e.X - oldX;
rect.X += e.X - oldX;
rect.Height += e.Y - oldY;
break;
case PosSizableRect.BottomMiddle:
rect.Height += e.Y - oldY;
break;
case PosSizableRect.RightUp:
rect.Width += e.X - oldX;
rect.Y += e.Y - oldY;
rect.Height -= e.Y - oldY;
break;
case PosSizableRect.RightBottom:
rect.Width += e.X - oldX;
rect.Height += e.Y - oldY;
break;
case PosSizableRect.RightMiddle:
rect.Width += e.X - oldX;
break;
case PosSizableRect.UpMiddle:
rect.Y += e.Y - oldY;
rect.Height -= e.Y - oldY;
break;
default:
if (mMove)
{
rect.X = rect.X + e.X - oldX;
rect.Y = rect.Y + e.Y - oldY;
}
break;
}
oldX = e.X;
oldY = e.Y;
if (rect.Width < 5 || rect.Height < 5)
{
rect = backupRect;
}
TestIfRectInsideArea();
mPictureBox.Invalidate();
}
private void TestIfRectInsideArea()
{
// Test if rectangle still inside the area.
if (rect.X < 0) rect.X = 0;
if (rect.Y < 0) rect.Y = 0;
if (rect.Width <= 0) rect.Width = 1;
if (rect.Height <= 0) rect.Height = 1;
if (rect.X + rect.Width > mPictureBox.Width)
{
rect.Width = mPictureBox.Width - rect.X - 1; // -1 to be still show
if (allowDeformingDuringMovement == false)
{
mIsClick = false;
}
}
if (rect.Y + rect.Height > mPictureBox.Height)
{
rect.Height = mPictureBox.Height - rect.Y - 1;// -1 to be still show
if (allowDeformingDuringMovement == false)
{
mIsClick = false;
}
}
}
private Rectangle CreateRectSizableNode(int x, int y)
{
return new Rectangle(x - sizeNodeRect / 2, y - sizeNodeRect / 2, sizeNodeRect, sizeNodeRect);
}
private Rectangle GetRect(PosSizableRect p)
{
switch (p)
{
case PosSizableRect.LeftUp:
return CreateRectSizableNode(rect.X, rect.Y);
case PosSizableRect.LeftMiddle:
return CreateRectSizableNode(rect.X, rect.Y + +rect.Height / 2);
case PosSizableRect.LeftBottom:
return CreateRectSizableNode(rect.X, rect.Y + rect.Height);
case PosSizableRect.BottomMiddle:
return CreateRectSizableNode(rect.X + rect.Width / 2, rect.Y + rect.Height);
case PosSizableRect.RightUp:
return CreateRectSizableNode(rect.X + rect.Width, rect.Y);
case PosSizableRect.RightBottom:
return CreateRectSizableNode(rect.X + rect.Width, rect.Y + rect.Height);
case PosSizableRect.RightMiddle:
return CreateRectSizableNode(rect.X + rect.Width, rect.Y + rect.Height / 2);
case PosSizableRect.UpMiddle:
return CreateRectSizableNode(rect.X + rect.Width / 2, rect.Y);
default:
return new Rectangle();
}
}
private PosSizableRect GetNodeSelectable(Point p)
{
foreach (PosSizableRect r in Enum.GetValues(typeof(PosSizableRect)))
{
if (GetRect(r).Contains(p))
{
return r;
}
}
return PosSizableRect.None;
}
private void ChangeCursor(Point p)
{
mPictureBox.Cursor = GetCursor(GetNodeSelectable(p));
}
/// <summary>
/// Get cursor for the handle
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
private Cursor GetCursor(PosSizableRect p)
{
switch (p)
{
case PosSizableRect.LeftUp:
return Cursors.SizeNWSE;
case PosSizableRect.LeftMiddle:
return Cursors.SizeWE;
case PosSizableRect.LeftBottom:
return Cursors.SizeNESW;
case PosSizableRect.BottomMiddle:
return Cursors.SizeNS;
case PosSizableRect.RightUp:
return Cursors.SizeNESW;
case PosSizableRect.RightBottom:
return Cursors.SizeNWSE;
case PosSizableRect.RightMiddle:
return Cursors.SizeWE;
case PosSizableRect.UpMiddle:
return Cursors.SizeNS;
default:
return Cursors.Default;
}
}
}
}
'C#' 카테고리의 다른 글
[이것이 C#이다. 3판] Chapter 05 코드의 흐름 제어하 연습문제 (0) | 2023.07.19 |
---|---|
C#이 지원하는 패턴 (0) | 2023.07.19 |
[이것이 C#이다. 3판] Chapter 04 데이터를 가공하는 연산 연습문제 (0) | 2023.07.17 |
[이것이 C#이다. 3판] Chapter 03 데이터 보관하기 연습문제 (0) | 2023.07.16 |
C# List를 JSON으로 변환하기 (0) | 2023.06.10 |