윈도우 애플리케이션에서는 마우스를 폼의 테두리나 모서리에 올리면 대응하는 리사이즈 커서가 나타나며, 드래그로 창 크기를 조절할 수 있습니다. Silverlight에서도 유사한 기능을 사용자 정의 컨트롤에 구현할 수 있으며, 이를 위해 Canvas 배치와 마우스 이벤트 처리를 적절히 조합해야 합니다.
1. Canvas 내부에 컨트롤 배치
크기 조절이 필요한 UI 요소는 반드시 Canvas 위에 위치해야 하며, 이는 Canvas.Left, Canvas.Top과 같은 첨부 속성을 통해 위치를 동적으로 제어할 수 있기 때문입니다. 절대 좌표 기반 레이아웃이 필요하므로 Grid나 StackPanel보다 Canvas가 적합합니다.
2. 마우스 위치 기반 커서 및 리사이즈 영역 감지
컨트롤 위에서 MouseMove 이벤트를 처리하여 마우스 포인터가 테두리 근처(예: 5픽셀 이내)에 있는지 판단하고, 그에 따라 커서 모양을 변경합니다. 아래는 개선된 로직 예시입니다:
private void OnMouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(this);
const double threshold = 5;
EdgeRegion currentRegion = EdgeRegion.Center;
// 모서리 및 테두리 영역 판별
bool nearLeft = position.X < threshold;
bool nearRight = Width - position.X < threshold;
bool nearTop = position.Y < threshold;
bool nearBottom = Height - position.Y < threshold;
if (nearTop && nearLeft)
{
Cursor = Cursors.SizeNWSE;
currentRegion = EdgeRegion.TopLeft;
}
else if (nearTop && nearRight)
{
Cursor = Cursors.SizeNESW;
currentRegion = EdgeRegion.TopRight;
}
else if (nearBottom && nearRight)
{
Cursor = Cursors.SizeNWSE;
currentRegion = EdgeRegion.BottomRight;
}
else if (nearBottom && nearLeft)
{
Cursor = Cursors.SizeNESW;
currentRegion = EdgeRegion.BottomLeft;
}
else if (nearLeft)
{
Cursor = Cursors.SizeWE;
currentRegion = EdgeRegion.Left;
}
else if (nearRight)
{
Cursor = Cursors.SizeWE;
currentRegion = EdgeRegion.Right;
}
else if (nearTop || nearBottom)
{
Cursor = Cursors.SizeNS;
currentRegion = (nearTop) ? EdgeRegion.Top : EdgeRegion.Bottom;
}
else
{
Cursor = Cursors.Arrow;
currentRegion = EdgeRegion.Center;
}
_currentEdge = currentRegion;
}
3. 마우스 드래그 시 크기 및 위치 업데이트
마우스 버튼 클릭(MouseLeftButtonDown) 시 초기 상태를 저장하고, MouseMove 중에 현재 마우스 위치와 기준점을 비교하여 크기 및 위치를 실시간 갱신합니다. 중요한 점은 상대 좌표 계산 시 부모 컨테이너(Canvas)를 기준으로 해야 한다는 것입니다.
private Point _startPoint;
private Size _originalSize;
private Point _originalLocation;
private bool _isResizing;
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (_currentEdge != EdgeRegion.Center)
{
_isResizing = true;
_startPoint = e.GetPosition(Parent as UIElement);
_originalSize = new Size(Width, Height);
_originalLocation = new Point(
(double)GetValue(Canvas.LeftProperty),
(double)GetValue(Canvas.TopProperty));
CaptureMouse();
e.Handled = true;
}
}
private void OnMouseMoveResize(object sender, MouseEventArgs e)
{
if (!_isResizing) return;
var currentPoint = e.GetPosition(Parent as UIElement);
double deltaX = currentPoint.X - _startPoint.X;
double deltaY = currentPoint.Y - _startPoint.Y;
double newWidth = _originalSize.Width;
double newHeight = _originalSize.Height;
double newLeft = _originalLocation.X;
double newTop = _originalLocation.Y;
const double minWidth = 10;
const double minHeight = 10;
switch (_currentEdge)
{
case EdgeRegion.Right:
newWidth = _originalSize.Width + deltaX;
break;
case EdgeRegion.Left:
newWidth = _originalSize.Width - deltaX;
newLeft = _originalLocation.X + deltaX;
break;
case EdgeRegion.Bottom:
newHeight = _originalSize.Height + deltaY;
break;
case EdgeRegion.Top:
newHeight = _originalSize.Height - deltaY;
newTop = _originalLocation.Y + deltaY;
break;
case EdgeRegion.TopLeft:
newWidth = _originalSize.Width - deltaX;
newHeight = _originalSize.Height - deltaY;
newLeft = _originalLocation.X + deltaX;
newTop = _originalLocation.Y + deltaY;
break;
case EdgeRegion.TopRight:
newWidth = _originalSize.Width + deltaX;
newHeight = _originalSize.Height - deltaY;
newTop = _originalLocation.Y + deltaY;
break;
case EdgeRegion.BottomLeft:
newWidth = _originalSize.Width - deltaX;
newHeight = _originalSize.Height + deltaY;
newLeft = _originalLocation.X + deltaX;
break;
case EdgeRegion.BottomRight:
newWidth = _originalSize.Width + deltaX;
newHeight = _originalSize.Height + deltaY;
break;
}
// 최소 크기 제한
if (newWidth >= minWidth) Width = newWidth;
if (newHeight >= minHeight) Height = newHeight;
if (newLeft != _originalLocation.X) SetValue(Canvas.LeftProperty, newLeft);
if (newTop != _originalLocation.Y) SetValue(Canvas.TopProperty, newTop);
}
4. 마우스 캡처 해제
MouseLeftButtonUp 이벤트에서 마우스 캡처를 해제하고 상태를 초기화합니다.
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isResizing)
{
_isResizing = false;
ReleaseMouseCapture();
}
}
중요 사항: 좌표계 기준점
크기 조절 중 마우스 위치를 얻을 때 반드시 부모 컨테이너를 기준으로 계산해야 합니다.
// 올바른 방식: 부모 기준 상대 좌표
var point = e.GetPosition(Parent as UIElement);
// 잘못된 방식: 자기 자신 기준으로는 위치 변화 반영 불가
var point = e.GetPosition(this);
자신을 기준으로 좌표를 얻으면 컨트롤의 크기 변화에 따라 좌표계가 변동되어 정확한 계산이 불가능해집니다. 따라서 항상 고정된 기준점인 부모 Canvas를 사용해야 합니다.