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

TestProject.zip
다운로드

▶ 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 Name="canvas"
        Background="Transparent">
        <Border Canvas.Left="50" Canvas.Top="30"
            Width="500"
            Height="300"
            BorderBrush="LightBlue"
            BorderThickness="5" />
        <Ellipse Canvas.Left="50" Canvas.Top="50"
            Stroke="Red"
            StrokeThickness="3"
            Width="200"
            Height="200">
            <Ellipse.Fill>
                <LinearGradientBrush
                    StartPoint="0 0"
                    EndPoint="0 1">
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Color="White" Offset="0" />
                        <GradientStop Color="Red"   Offset="1" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Polygon
            Stroke="Green"
            StrokeThickness="3"
            Points="150 150 250 250 150 350 50 250 150 150">
            <Polygon.Fill>
                <LinearGradientBrush
                    StartPoint="0 0"
                    EndPoint="0 1">
                    <GradientStop Color="White" Offset="0" />
                    <GradientStop Color="Green" Offset="1" />
                </LinearGradientBrush>
            </Polygon.Fill>
        </Polygon>
        <Rectangle Canvas.Left="200" Canvas.Top="250"
            Width="200"
            Height="200"
            Stroke="Blue"
            StrokeThickness="3">
            <Rectangle.Fill>
                <LinearGradientBrush
                    StartPoint="0 0"
                    EndPoint="0 1">
                    <GradientStop Color="White" Offset="0" />
                    <GradientStop Color="Blue"  Offset="1" />
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
    </Canvas>
</Window>

 

728x90

 

▶ MainWindow.xaml.cs

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace TestProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 히트 타입 - HitType

        /// <summary>
        /// 히트 타입
        /// </summary>
        private enum HitType
        {
            /// <summary>
            /// 해당 무
            /// </summary>
            None,
            
            /// <summary>
            /// 몸체
            /// </summary>
            Body,
            
            /// <summary>
            /// 좌상단
            /// </summary>
            UpperLeft,
            
            /// <summary>
            /// 우상단
            /// </summary>
            UpperRight,
            
            /// <summary>
            /// 우하단
            /// </summary>
            LowerRight,
            
            /// <summary>
            /// 좌하단
            /// </summary>
            LowerLeft,
            
            /// <summary>
            /// 왼쪽
            /// </summary>
            Left,
            
            /// <summary>
            /// 오른쪽
            /// </summary>
            Right,
            
            /// <summary>
            /// 위쪽
            /// </summary>
            Top,
            
            /// <summary>
            /// 아래쪽
            /// </summary>
            Bottom
        };

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 드래그 여부
        /// </summary>
        private bool isDragging = false;

        /// <summary>
        /// 마지막 포인트
        /// </summary>
        private Point lastPoint;

        /// <summary>
        /// 히트 타입
        /// </summary>
        private HitType hitType = HitType.None;

        /// <summary>
        /// 히트 도형
        /// </summary>
        private Shape hitShape = null;

        /// <summary>
        /// 도형 리스트
        /// </summary>
        private List<Shape> shapeList;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            Loaded                += Window_Loaded;
            this.canvas.MouseDown += canvas_MouseDown;
            this.canvas.MouseMove += canvas_MouseMove;
            this.canvas.MouseUp   += canvas_MouseUp;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 윈도우 로드시 처리하기 - Window_Loaded(sender, e)

        /// <summary>
        /// 윈도우 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.shapeList = new List<Shape>();

            foreach(UIElement child in canvas.Children)
            {
                if(child is Shape)
                {
                    this.shapeList.Add(child as Shape);
                }
            }

            this.shapeList.Reverse();
        }

        #endregion
        #region 캔버스 마우스 DOWN 처리하기 - canvas_MouseDown(sender, e)

        /// <summary>
        /// 캔버스 마우스 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            FindHit(Mouse.GetPosition(this.canvas));

            SetMouseCursor();

            if(this.hitType == HitType.None)
            {
                return;
            }

            this.lastPoint = Mouse.GetPosition(this.canvas);

            this.isDragging = true;
        }

        #endregion
        #region 캔버스 마우스 이동시 처리하기 - canvas_MouseMove(sender, e)

        /// <summary>
        /// 캔버스 마우스 이동시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if(!this.isDragging)
            {
                FindHit(Mouse.GetPosition(this.canvas));

                SetMouseCursor();
            }
            else
            {
                ResizeShape();
            }
        }

        #endregion
        #region 캔버스 마우스 UP 처리하기 - canvas_MouseUp(sender, e)

        /// <summary>
        /// 캔버스 마우스 UP 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            this.isDragging = false;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 상하좌우 설정하기 - SetLRTB(shape, left, right, top, bottom)

        /// <summary>
        /// 상하좌우 설정하기
        /// </summary>
        /// <param name="shape">도형</param>
        /// <param name="left">왼쪽</param>
        /// <param name="right">오른쪽</param>
        /// <param name="top">위쪽</param>
        /// <param name="bottom">아래쪽</param>
        private void SetLRTB(Shape shape, out double left, out double right, out double top, out double bottom)
        {
            if(!(shape is Polygon))
            {
                left   = Canvas.GetLeft(shape);
                top    = Canvas.GetTop (shape);
                right  = left + shape.ActualWidth;
                bottom = top  + shape.ActualHeight;

                return;
            }

            Polygon polygon = shape as Polygon;

            left   = polygon.Points[0].X;
            right  = left;
            top    = polygon.Points[0].Y;
            bottom = top;

            foreach(Point point in polygon.Points)
            {
                if(left > point.X)
                {
                    left = point.X;
                }

                if(right < point.X)
                {
                    right = point.X;
                }

                if(top > point.Y)
                {
                    top = point.Y;
                }

                if(bottom < point.Y)
                {
                    bottom = point.Y;
                }
            }
        }

        #endregion
        #region 히트 타입 구하기 - GetHitType(shape, point)

        /// <summary>
        /// 히트 타입 구하기
        /// </summary>
        /// <param name="shape">도형</param>
        /// <param name="point">포인트</param>
        /// <returns>히트 타입</returns>
        private HitType GetHitType(Shape shape, Point point)
        {
            double left;
            double right;
            double top;
            double bottom;

            SetLRTB(shape, out left, out right, out top, out bottom);

            if(point.X < left)
            {
                return HitType.None;
            }

            if(point.X > right)
            {
                return HitType.None;
            }

            if(point.Y < top)
            {
                return HitType.None;
            }

            if(point.Y > bottom)
            {
                return HitType.None;
            }

            const double GAP = 10;

            if(point.X - left < GAP)
            {
                if(point.Y - top < GAP)
                {
                    return HitType.UpperLeft;
                }

                if(bottom - point.Y < GAP)
                {
                    return HitType.LowerLeft;
                }

                return HitType.Left;
            }

            if(right - point.X < GAP)
            {
                if(point.Y - top < GAP)
                {
                    return HitType.UpperRight;
                }

                if(bottom - point.Y < GAP)
                {
                    return HitType.LowerRight;
                }

                return HitType.Right;
            }

            if(point.Y - top < GAP)
            {
                return HitType.Top;
            }

            if(bottom - point.Y < GAP)
            {
                return HitType.Bottom;
            }

            return HitType.Body;
        }

        #endregion
        #region 히트 찾기 - FindHit(point)

        /// <summary>
        /// 히트 찾기
        /// </summary>
        /// <param name="point">포인트</param>
        private void FindHit(Point point)
        {
            this.hitShape = null;
            this.hitType  = HitType.None;

            foreach(Shape shape in this.shapeList)
            {
                this.hitType = GetHitType(shape, point);

                if(this.hitType != HitType.None)
                {
                    this.hitShape = shape;

                    return;
                }
            }

            return;
        }

        #endregion
        #region 마우스 커서 설정하기 - SetMouseCursor()

        /// <summary>
        /// 마우스 커서 설정하기
        /// </summary>
        private void SetMouseCursor()
        {
            Cursor cursor = Cursors.Arrow;

            switch(this.hitType)
            {
                case HitType.None       : cursor = Cursors.Arrow;     break;
                case HitType.Body       : cursor = Cursors.ScrollAll; break;
                case HitType.UpperLeft  :
                case HitType.LowerRight : cursor = Cursors.SizeNWSE;  break;
                case HitType.LowerLeft  :
                case HitType.UpperRight : cursor = Cursors.SizeNESW;  break;
                case HitType.Top        :
                case HitType.Bottom     : cursor = Cursors.SizeNS;    break;
                case HitType.Left       :
                case HitType.Right      : cursor = Cursors.SizeWE;    break;
            }

            if(Cursor != cursor)
            {
                Cursor = cursor;
            }
        }

        #endregion
        #region 다각형 업데이트하기 - UpdatePolygon(left, right, top, bottom, newX, newY, newWidth, newHeight)

        /// <summary>
        /// 다각형 업데이트하기
        /// </summary>
        /// <param name="left">왼쪽</param>
        /// <param name="right">오른쪽</param>
        /// <param name="top">위쪽</param>
        /// <param name="bottom">아래쪽</param>
        /// <param name="newX">신규 X</param>
        /// <param name="newY">신규 Y</param>
        /// <param name="newWidth">신규 너비</param>
        /// <param name="newHeight">신규 높이</param>
        private void UpdatePolygon
        (
            double left,
            double right,
            double top,
            double bottom,
            double newX,
            double newY,
            double newWidth,
            double newHeight
        )
        {
            double scaleX = newWidth  / (right  - left);
            double scaleY = newHeight / (bottom - top );
            
            Polygon polygon = this.hitShape as Polygon;

            List<Point> newPointList = new List<Point>();

            foreach(Point point in polygon.Points)
            {
                double x = newX + scaleX * (point.X - left);
                double y = newY + scaleY * (point.Y - top );

                newPointList.Add(new Point(x, y));
            }

            polygon.Points = new PointCollection(newPointList);
        }

        #endregion
        #region 도형 크기 변경하기 - ResizeShape()

        /// <summary>
        /// 도형 크기 변경하기
        /// </summary>
        private void ResizeShape()
        {
            Point point = Mouse.GetPosition(this.canvas);

            double offsetX = point.X - this.lastPoint.X;
            double offsetY = point.Y - this.lastPoint.Y;

            double left;
            double right;
            double top;
            double bottom;

            SetLRTB(this.hitShape, out left, out right, out top, out bottom);

            double newX      = left;
            double newY      = top;
            double newWidth  = right  - left;
            double newHeight = bottom - top;

            switch(this.hitType)
            {
                case HitType.Body :

                    newX += offsetX;
                    newY += offsetY;

                    break;

                case HitType.UpperLeft :

                    newX += offsetX;
                    newY += offsetY;

                    newWidth  -= offsetX;
                    newHeight -= offsetY;

                    break;

                case HitType.UpperRight :

                    newY += offsetY;

                    newWidth  += offsetX;
                    newHeight -= offsetY;

                    break;

                case HitType.LowerRight :

                    newWidth  += offsetX;
                    newHeight += offsetY;

                    break;

                case HitType.LowerLeft :

                    newX += offsetX;

                    newWidth  -= offsetX;
                    newHeight += offsetY;

                    break;

                case HitType.Left :

                    newX += offsetX;

                    newWidth -= offsetX;

                    break;

                case HitType.Right :

                    newWidth += offsetX;

                    break;

                case HitType.Bottom :

                    newHeight += offsetY;

                    break;

                case HitType.Top :

                    newY += offsetY;

                    newHeight -= offsetY;

                    break;
            }

            if((newWidth <= 0) || (newHeight <= 0))
            {
                return;
            }

            if(this.hitShape is Polygon)
            {
                UpdatePolygon(left, right, top, bottom, newX, newY, newWidth, newHeight);
            }
            else
            {
                Canvas.SetLeft(this.hitShape, newX);
                Canvas.SetTop (this.hitShape, newY);

                this.hitShape.Width  = newWidth;
                this.hitShape.Height = newHeight;
            }

            this.lastPoint = point;
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요