첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

■ 마우스를 사용해 엘리먼트를 이동/회전/확대/축소하는 방법을 보여준다.

TestProject.zip
7.62MB

▶ 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
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요