윈폼 애플리케이션 개발 시, 사용자 인터페이스의 유연한 조정을 위해 컨트롤의 경계를 드래그하여 크기를 변경하는 기능이 필요할 수 있습니다. 아래는
DataGridView와 Panel 등 다양한 컨트롤에 대해 마우스로 왼쪽, 오른쪽, 위, 아래 또는 모든 방향으로 크기 조절이 가능한 확장 메서드를 제공하는 예제입니다.
사용 방법은 간단합니다. 폼 로드 시 각 컨트롤에 해당 메서드를 호출하여 이벤트를 연결하면 됩니다.
private void Form1_Load(object sender, EventArgs e)
{
panelLeft.SetResizeHandler(this, ResizeDirection.Left);
panelRight.SetResizeHandler(this, ResizeDirection.Right);
panelTop.SetResizeHandler(this, ResizeDirection.Top);
panelBottom.SetResizeHandler(this, ResizeDirection.Bottom);
dataGridView1.SetResizeHandler(this, ResizeDirection.All);
}
다음은 핵심 로직을 포함한 전체 코드입니다. 기본적으로 마우스 위치를 기반으로 컨트롤의 경계선 근처인지 판단하고, 드래그 시 크기 조정을 처리하며, 최소/최대 크기 제한도 지원합니다.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ResizeControlHelper
{
public static class ControlExtensions
{
public static void SetResizeHandler(this Control control, Form owner, ResizeDirection direction, Size? maxSize = null, Size? minSize = null)
{
new ResizeManager(control, owner, direction, maxSize, minSize);
}
}
public enum ResizeDirection
{
Left,
Right,
Top,
Bottom,
All
}
public class ResizeManager
{
private readonly Control targetControl;
private readonly Form parentForm;
private readonly ResizeDirection direction;
private readonly Size maxLimit;
private readonly Size minLimit;
private bool isDragging;
private ResizeDirection currentDirection;
public ResizeManager(Control ctrl, Form form, ResizeDirection dir, Size? max, Size? min)
{
targetControl = ctrl;
parentForm = form;
direction = dir;
maxLimit = max ?? new Size(1000, 1000);
minLimit = min ?? new Size(100, 100);
// 이벤트 연결
targetControl.MouseDown += OnMouseDown;
targetControl.MouseMove += OnMouseMove;
targetControl.MouseUp += OnMouseUp;
targetControl.MouseLeave += OnMouseLeave;
// 초기 커서 설정
if (dir == ResizeDirection.All) targetControl.Cursor = Cursors.SizeAll;
else if (dir == ResizeDirection.Left || dir == ResizeDirection.Right) targetControl.Cursor = Cursors.SizeWE;
else if (dir == ResizeDirection.Top || dir == ResizeDirection.Bottom) targetControl.Cursor = Cursors.SizeNS;
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
if (direction == ResizeDirection.All)
{
var pos = Control.MousePosition;
var screenLoc = parentForm.PointToScreen(targetControl.Location);
// 왼쪽 경계 확인
if (pos.X >= screenLoc.X - 5 && pos.X <= screenLoc.X + 5 && pos.Y >= screenLoc.Y)
currentDirection = ResizeDirection.Left;
// 위쪽 경계 확인
else if (pos.Y >= screenLoc.Y - 5 && pos.Y <= screenLoc.Y + 5 && pos.X >= screenLoc.X)
currentDirection = ResizeDirection.Top;
// 오른쪽 경계 확인
else if (pos.X >= screenLoc.X + targetControl.Width - 5 && pos.X <= screenLoc.X + targetControl.Width + 5 && pos.Y >= screenLoc.Y)
currentDirection = ResizeDirection.Right;
// 아래쪽 경계 확인
else if (pos.Y >= screenLoc.Y + targetControl.Height - 5 && pos.Y <= screenLoc.Y + targetControl.Height + 5 && pos.X >= screenLoc.X)
currentDirection = ResizeDirection.Bottom;
else return;
isDragging = true;
targetControl.Capture = true;
}
else
{
isDragging = true;
currentDirection = direction;
}
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (!isDragging) return;
var mousePos = Control.MousePosition;
var screenLoc = parentForm.PointToScreen(targetControl.Location);
switch (currentDirection)
{
case ResizeDirection.Left:
if (mousePos.X < screenLoc.X + targetControl.Width - minLimit.Width)
UpdateSize(-e.X, 0);
break;
case ResizeDirection.Right:
if (mousePos.X > screenLoc.X + targetControl.Width + minLimit.Width)
UpdateSize(e.X, 0);
break;
case ResizeDirection.Top:
if (mousePos.Y < screenLoc.Y + targetControl.Height - minLimit.Height)
UpdateSize(0, -e.Y);
break;
case ResizeDirection.Bottom:
if (mousePos.Y > screenLoc.Y + targetControl.Height + minLimit.Height)
UpdateSize(0, e.Y);
break;
default:
break;
}
}
private void UpdateSize(int widthDelta, int heightDelta)
{
var newWidth = targetControl.Width + widthDelta;
var newHeight = targetControl.Height + heightDelta;
if (newWidth < minLimit.Width || newWidth > maxLimit.Width ||
newHeight < minLimit.Height || newHeight > maxLimit.Height)
return;
if (widthDelta != 0)
{
if (currentDirection == ResizeDirection.Left)
{
targetControl.Width = newWidth;
targetControl.Left -= widthDelta;
}
else
{
targetControl.Width = newWidth;
}
}
if (heightDelta != 0)
{
if (currentDirection == ResizeDirection.Top)
{
targetControl.Height = newHeight;
targetControl.Top -= heightDelta;
}
else
{
targetControl.Height = newHeight;
}
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
isDragging = false;
targetControl.Capture = false;
targetControl.Cursor = Cursors.Default;
}
private void OnMouseLeave(object sender, EventArgs e)
{
if (targetControl.Cursor == Cursors.SizeWE || targetControl.Cursor == Cursors.SizeNS)
targetControl.Cursor = Cursors.Default;
}
}
}
이 구현은 여러 컨트롤에 대해 동일한 동작을 적용할 수 있도록 확장 가능하며, 스레드 안전성과 크기 제약 조건을 고려해 설계되었습니다. 실제 사용 시 `minSize`와 `maxSize`를 통해 화면 내 정합성을 유지할 수 있습니다.