728x90
반응형
728x170
■ 마우스를 사용해 엘리먼트를 이동/회전/확대/축소하는 방법을 보여준다.
▶ MousePoint.cs
using System.Windows;
namespace TestProject
{
/// <summary>
/// 마우스 포인트
/// </summary>
public struct MousePoint
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 키 - Key
/// <summary>
/// 키
/// </summary>
public int Key;
#endregion
#region 포인트 - Point
/// <summary>
/// 포인트
/// </summary>
public Point Point;
#endregion
}
}
▶ MouseAdorner.cs
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace TestProject
{
/// <summary>
/// 마우스 어도너
/// </summary>
public class MouseAdorner : Adorner
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 마우스 포인트 딕셔너리
/// </summary>
private Dictionary<int, MousePoint> mousePointDictionary;
/// <summary>
/// 스트로크 펜
/// </summary>
private Pen strokePen = new Pen(Brushes.Gray, 1);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MouseAdorner(element)
/// <summary>
/// 생성자
/// </summary>
/// <param name="element">엘리먼트</param>
public MouseAdorner(UIElement element) : base(element)
{
this.mousePointDictionary = new Dictionary<int, MousePoint>();
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(element);
adornerLayer.Add(this);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 객체 추가하기 - AddObject(mousePoint)
/// <summary>
/// 객체 추가하기
/// </summary>
/// <param name="mousePoint">마우스 포인트</param>
public void AddObject(MousePoint mousePoint)
{
this.mousePointDictionary[mousePoint.Key] = mousePoint;
InvalidateVisual();
}
#endregion
#region 객체 제거하기 - RemoveObject(key)
/// <summary>
/// 객체 제거하기
/// </summary>
/// <param name="key">키</param>
public void RemoveObject(int key)
{
if(this.mousePointDictionary.ContainsKey(key))
{
this.mousePointDictionary.Remove(key);
InvalidateVisual();
}
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 렌더링시 처리하기 - OnRender(drawingContext)
/// <summary>
/// 렌더링시 처리하기
/// </summary>
/// <param name="drawingContext">드로잉 컨텍스트</param>
protected override void OnRender(DrawingContext drawingContext)
{
foreach(MousePoint mousePoint in this.mousePointDictionary.Values)
{
if(mousePoint.Key == 0)
{
drawingContext.DrawEllipse
(
Brushes.Tomato,
this.strokePen,
new Point(mousePoint.Point.X, mousePoint.Point.Y),
10.0,
10.0
);
}
else
{
drawingContext.DrawEllipse
(
Brushes.Turquoise,
this.strokePen,
new Point(mousePoint.Point.X, mousePoint.Point.Y),
10.0,
10.0
);
}
}
IsHitTestVisible = false;
base.OnRender(drawingContext);
}
#endregion
}
}
▶ Transform.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace TestProject
{
/// <summary>
/// 변환
/// </summary>
public class Transform
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 마우스 어도너
/// </summary>
private static MouseAdorner _mouseAdorner = null;
/// <summary>
/// 첫번째 마우스 포인트
/// </summary>
private static MousePoint _firstMousePoint;
/// <summary>
/// 두번째 마우스 포인트
/// </summary>
private static MousePoint _secondMousePoint;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 윈도우
/// </summary>
private Window window;
/// <summary>
/// 엘리먼트
/// </summary>
private FrameworkElement element;
/// <summary>
/// 변환 그룹
/// </summary>
private TransformGroup transformGroup = new TransformGroup();
/// <summary>
/// 회전 변환
/// </summary>
private RotateTransform rotateTransform = new RotateTransform();
/// <summary>
/// 스케일 변환
/// </summary>
private ScaleTransform scaleTransform = new ScaleTransform();
/// <summary>
/// 이동 변환
/// </summary>
private TranslateTransform translateTransform = new TranslateTransform();
/// <summary>
/// 드래그 여부
/// </summary>
private bool isDragging = false;
/// <summary>
/// 스케일 여부
/// </summary>
private bool isScaling = false;
/// <summary>
/// 회전 여부
/// </summary>
private bool isRotating = false;
/// <summary>
/// 첫번째 길이
/// </summary>
private double firstLength;
/// <summary>
/// 첫번째 각도
/// </summary>
private double firstAngle;
/// <summary>
/// 역변환 첫번째 위치
/// </summary>
private Point inverseFirstPoint;
/// <summary>
/// 첫번째 위치
/// </summary>
private Point firstPoint;
/// <summary>
/// 변환 여부
/// </summary>
private bool isTransformation = false;
/// <summary>
/// 이전 Z 인덱스
/// </summary>
private int previousZIndex;
/// <summary>
/// 드래그 가능 여부
/// </summary>
public bool canDrag = false;
/// <summary>
/// 스케일 가능 여부
/// </summary>
public bool canScale = false;
/// <summary>
/// 회전 가능 여부
/// </summary>
public bool canRotate = false;
/// <summary>
/// 위치 표시 여부
/// </summary>
public bool showPosition = false;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - Transform(window, element)
/// <summary>
/// 생성자
/// </summary>
/// <param name="window">윈도우</param>
/// <param name="element">엘리먼트</param>
public Transform(Window window, FrameworkElement element)
{
this.window = window;
this.element = element;
this.element.PreviewMouseDown += element_PreviewMouseDown;
this.element.PreviewMouseUp += element_PreviewMouseUp;
this.element.MouseLeave += element_MouseLeave;
this.transformGroup.Children.Add(this.rotateTransform );
this.transformGroup.Children.Add(this.scaleTransform );
this.transformGroup.Children.Add(this.translateTransform);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
//////////////////////////////////////////////////////////////////////////////// Event
#region 엘리먼트 PREVIEW 마우스 DOWN 처리하기 - element_PreviewMouseDown(sender, e)
/// <summary>
/// 엘리먼트 PREVIEW 마우스 DOWN 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if(e.LeftButton == MouseButtonState.Pressed && e.RightButton == MouseButtonState.Pressed && (this.canScale || this.canRotate))
{
this.isTransformation = !this.isTransformation;
this.window.PreviewMouseMove -= window_DragPreviewMouseMove;
if(this.isTransformation)
{
this.firstPoint = e.GetPosition(this.window);
this.window.PreviewMouseMove += window_TransformPreviewMouseMove;
}
}
else
{
if(this.canDrag && e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed)
{
if(!this.isTransformation)
{
this.firstPoint = e.GetPosition(this.window);
this.window.PreviewMouseMove += window_DragPreviewMouseMove;
this.previousZIndex = Canvas.GetZIndex(this.element);
Canvas.SetZIndex(this.element, 20);
}
}
}
if(this.showPosition)
{
if(!this.isTransformation)
{
ChangePosition(_firstMousePoint, e.GetPosition(this.window));
}
}
}
#endregion
#region 엘리먼트 PREVIEW 마우스 UP 처리하기 - element_PreviewMouseUp(sender, e)
/// <summary>
/// 엘리먼트 PREVIEW 마우스 UP 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void element_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if(e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released)
{
if(!this.isTransformation)
{
Canvas.SetZIndex(this.element, this.previousZIndex);
}
if(this.canDrag)
{
this.window.PreviewMouseMove -= window_DragPreviewMouseMove;
this.isDragging = false;
}
if (this.canScale || this.canRotate)
{
if(!this.isTransformation)
{
this.window.PreviewMouseMove -= window_TransformPreviewMouseMove;
}
this.isRotating = this.isScaling = false;
}
if(this.showPosition)
{
if(!this.isTransformation)
{
_mouseAdorner.RemoveObject(_firstMousePoint.Key );
_mouseAdorner.RemoveObject(_secondMousePoint.Key);
}
else
{
_mouseAdorner.RemoveObject(_secondMousePoint.Key);
}
}
}
}
#endregion
#region 엘리먼트 마우스 이탈시 처리하기 - element_MouseLeave(sender, e)
/// <summary>
/// 엘리먼트 마우스 이탈시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void element_MouseLeave(object sender, MouseEventArgs e)
{
if(e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released)
{
this.window.PreviewMouseMove -= window_DragPreviewMouseMove;
this.window.PreviewMouseMove -= window_TransformPreviewMouseMove;
Canvas.SetZIndex(this.element, this.previousZIndex);
this.isTransformation = this.isDragging = this.isRotating = this.isScaling = false;
if(this.showPosition)
{
if (_mouseAdorner != null)
{
_mouseAdorner.RemoveObject(_firstMousePoint.Key );
_mouseAdorner.RemoveObject(_secondMousePoint.Key);
}
}
}
}
#endregion
#region 윈도우 드래그 PREVIEW 마우스 이동시 처리하기 - window_DragPreviewMouseMove(sender, e)
/// <summary>
/// 윈도우 드래그 PREVIEW 마우스 이동시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void window_DragPreviewMouseMove(object sender, MouseEventArgs e)
{
if(e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed)
{
Point currentPoint = e.GetPosition(this.window);
if(this.showPosition)
{
ChangePosition(_firstMousePoint, currentPoint);
}
Drag(currentPoint);
}
}
#endregion
#region 윈도우 변환 PREVIEW 마우스 이동시 처리하기 - window_TransformPreviewMouseMove(sender, e)
/// <summary>
/// 윈도우 변환 PREVIEW 마우스 이동시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void window_TransformPreviewMouseMove(object sender, MouseEventArgs e)
{
if(e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed)
{
Point currentPoint = e.GetPosition(this.window);
Point distanceVector = new Point((currentPoint.X - this.firstPoint.X), (this.firstPoint.Y - currentPoint.Y));
double currentLength = Math.Sqrt(distanceVector.X * distanceVector.X + distanceVector.Y * distanceVector.Y);
double currentAngle = Math.Atan2(distanceVector.Y, distanceVector.X) * 180 / Math.PI;
if(this.showPosition)
{
ChangePosition(_secondMousePoint, currentPoint);
}
if(this.canScale)
{
if(!this.isScaling)
{
this.firstLength = currentLength / this.scaleTransform.ScaleX;
this.isScaling = true;
}
if(this.firstLength > 0)
{
double scale = currentLength / this.firstLength;
this.scaleTransform.ScaleX = scale;
this.scaleTransform.ScaleY = scale;
}
}
if(this.canRotate)
{
if(!this.isRotating)
{
this.firstAngle = currentAngle + this.rotateTransform.Angle;
this.isRotating = true;
}
double finalAngle = (this.firstAngle - currentAngle + 360) % 360;
this.rotateTransform.Angle = finalAngle;
}
Drag(this.firstPoint);
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region 드래그하기 - Drag(point)
/// <summary>
/// 드래그하기
/// </summary>
/// <param name="point">포인트</param>
private void Drag(Point point)
{
if(!this.isDragging)
{
this.inverseFirstPoint = this.element.TransformToVisual(this.window).Inverse.Transform(this.firstPoint);
this.isDragging = true;
}
Point translatePoint = this.element.TranslatePoint(this.inverseFirstPoint, this.window);
Point differencePoint = new Point(point.X - translatePoint.X, point.Y - translatePoint.Y);
this.translateTransform.X += differencePoint.X;
this.translateTransform.Y += differencePoint.Y;
this.element.RenderTransform = this.transformGroup;
}
#endregion
#region 위치 변경하기 - ChangePosition(mousePoint, point)
/// <summary>
/// 위치 변경하기
/// </summary>
/// <param name="mousePoint">마우스 포인트</param>
/// <param name="point">포인트</param>
private void ChangePosition(MousePoint mousePoint, Point point)
{
if(_mouseAdorner == null)
{
_mouseAdorner = new MouseAdorner((UIElement)this.window.Content);
_firstMousePoint.Key = 0;
_secondMousePoint.Key = 1;
}
mousePoint.Point = point;
_mouseAdorner.AddObject(mousePoint);
}
#endregion
}
}
▶ MainWindow.xaml
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800"
Height="600"
Title="마우스를 사용해 엘리먼트 이동/회전/확대/축소하기"
FontFamily="나눔고딕코딩"
FontSize="16">
<Canvas Background="DarkGray">
<Grid Name="grid" Canvas.Left="20" Canvas.Top="20"
Height="150"
Width="150"
Background="Black" />
<Image Name="image1" Canvas.Left="180" Canvas.Top="170"
Width="300"
Height="200"
Stretch="Fill"
Source="IMAGE/sample1.png" />
<Button Name="button" Canvas.Left="390" Canvas.Top="60"
Height="100"
Width="100">
<Button.Background>
<LinearGradientBrush
StartPoint="0.5 0"
EndPoint="0.5 1">
<GradientStop Offset="0" Color="#ffffffff" />
<GradientStop Offset="0.9" Color="#ff68e5db" />
</LinearGradientBrush>
</Button.Background>
</Button>
<Border Name="border" Canvas.Left="500" Canvas.Top="60"
Width="200"
BorderThickness="2 2 2 2"
BorderBrush="#ffffee00">
<Image Name="image2"
Width="Auto"
Height="Auto"
Stretch="Fill"
Source="IMAGE/sample2.png" />
</Border>
</Canvas>
</Window>
▶ MainWindow.xaml.cs
using System.Windows;
namespace TestProject
{
/// <summary>
/// 메인 윈도우
/// </summary>
public partial class MainWindow : Window
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 변환
/// </summary>
private Transform transform;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainWindow()
/// <summary>
/// 생성자
/// </summary>
public MainWindow()
{
InitializeComponent();
Loaded += Window_Loaded;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 윈도우 로드시 처리하기 - Window_Loaded(sender, e)
/// <summary>
/// 윈도우 로드시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.transform = new Transform(this, this.button);
this.transform.canDrag = true;
this.transform.canScale = true;
this.transform.canRotate = true;
this.transform.showPosition = true;
this.transform = new Transform(this, this.border);
this.transform.canDrag = true;
this.transform.canScale = true;
this.transform.canRotate = true;
this.transform.showPosition = true;
this.transform = new Transform(this, this.image1);
this.transform.canDrag = true;
this.transform.canScale = true;
this.transform.canRotate = true;
this.transform.showPosition = true;
this.transform = new Transform(this, this.grid);
this.transform.canDrag = true;
this.transform.canScale = true;
this.transform.canRotate = true;
this.transform.showPosition = true;
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] DependencyPropertyHelper 클래스 : GetValueSource 정적 메소드를 사용해 의존 속성 값 소스 구하기 (0) | 2021.02.21 |
---|---|
[C#/WPF] x:Array 엘리먼트 : 배열 사용하기 (0) | 2021.02.21 |
[C#/WPF] x:Reference 태그 확장 사용하기 (0) | 2021.02.21 |
[C#/WPF] x:Shared 속성 사용하기 (0) | 2021.02.20 |
[C#/WPF] Shape 클래스 : 환형 진행바 사용하기 (0) | 2021.02.20 |
[C#/WPF] EventTrigger 클래스 : 코드로 이벤트 트리거 사용하기 (0) | 2021.02.20 |
[C#/WPF] Image 클래스 : 마우스를 사용해 이미지 회전시키기 (0) | 2021.02.20 |
[C#/WPF] ComponentDispatcher 클래스 : ThreadFilterMessage 정적 이벤트를 사용해 프로세스 간 메시지 송수신하기 (0) | 2021.02.20 |
[C#/WPF] Slider 클래스 : 커스텀 슬라이더 사용하기 (0) | 2021.02.20 |
[C#/WPF] Window 클래스 : 반투명 윈도우 사용하기 (0) | 2021.02.19 |
댓글을 달아 주세요