첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

TestProject.zip
다운로드

▶ MoveThumb.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 이동 썸
    /// </summary>
    public class MoveThumb : Thumb
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MoveThumb

        /// <summary>
        /// 생성자
        /// </summary>
        public MoveThumb()
        {
            DragDelta += Thumb_DragDelta;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 썸 드래그 델타 처리하기 - Thumb_DragDelta(sender, e)

        /// <summary>
        /// 썸 드래그 델타 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            ContentControl designerItem = DataContext as ContentControl;

            if(designerItem != null)
            {
                Point dragDeltaPoint = new Point(e.HorizontalChange, e.VerticalChange);

                RotateTransform rotateTransform = designerItem.RenderTransform as RotateTransform;

                if(rotateTransform != null)
                {
                    dragDeltaPoint = rotateTransform.Transform(dragDeltaPoint);
                }

                Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + dragDeltaPoint.X);
                Canvas.SetTop (designerItem, Canvas.GetTop (designerItem) + dragDeltaPoint.Y);
            }
        }

        #endregion
    }
}

 

▶ ResizeThumb.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 크기 조정 썸
    /// </summary>
    public class ResizeThumb : Thumb
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 각도
        /// </summary>
        private double angle;

        /// <summary>
        /// 변환 원점
        /// </summary>
        private Point transformOrigin;

        /// <summary>
        /// 디자이너 항목
        /// </summary>
        private ContentControl designerItem;

        #endregion

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

        #region 생성자 - ResizeThumb()

        /// <summary>
        /// 생성자
        /// </summary>
        public ResizeThumb()
        {
            DragStarted += Thumb_DragStarted;
            DragDelta   += Thumb_DragDelta;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 썸 드래그 시작시 처리하기 - Thumb_DragStarted(sender, e)

        /// <summary>
        /// 썸 드래그 시작시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
        {
            this.designerItem = DataContext as ContentControl;

            if(this.designerItem != null)
            {
                this.transformOrigin = this.designerItem.RenderTransformOrigin;

                RotateTransform rotateTransform = this.designerItem.RenderTransform as RotateTransform;

                if(rotateTransform != null)
                {
                    this.angle = rotateTransform.Angle * Math.PI / 180.0;
                }
                else
                {
                    this.angle = 0;
                }
            }
        }

        #endregion
        #region 썸 드래그 델타 처리하기 - Thumb_DragDelta(sender, e)

        /// <summary>
        /// 썸 드래그 델타 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            if(this.designerItem != null)
            {
                double deltaY;
                double deltaX;

                switch(VerticalAlignment)
                {
                    case VerticalAlignment.Bottom :

                        deltaY = Math.Min(-e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight);

                        Canvas.SetTop
                        (
                            this.designerItem,
                            Canvas.GetTop(this.designerItem) + (this.transformOrigin.Y * deltaY * (1 - Math.Cos(-this.angle)))
                        );

                        Canvas.SetLeft
                        (
                            this.designerItem,
                            Canvas.GetLeft(this.designerItem) - deltaY * this.transformOrigin.Y * Math.Sin(-this.angle)
                        );

                        this.designerItem.Height -= deltaY;

                        break;

                    case VerticalAlignment.Top :

                        deltaY = Math.Min(e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight);

                        Canvas.SetTop
                        (
                            this.designerItem,
                            Canvas.GetTop(this.designerItem) + deltaY * Math.Cos(-this.angle) +
                                (this.transformOrigin.Y * deltaY * (1 - Math.Cos(-this.angle)))
                        );

                        Canvas.SetLeft
                        (
                            this.designerItem,
                            Canvas.GetLeft(this.designerItem) + deltaY * Math.Sin(-this.angle) -
                                (this.transformOrigin.Y * deltaY * Math.Sin(-this.angle))
                        );

                        this.designerItem.Height -= deltaY;

                        break;

                    default :

                        break;
                }

                switch(HorizontalAlignment)
                {
                    case HorizontalAlignment.Left :

                        deltaX = Math.Min(e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth);

                        Canvas.SetTop
                        (
                            this.designerItem,
                            Canvas.GetTop(this.designerItem) + deltaX * Math.Sin(this.angle) -
                                this.transformOrigin.X * deltaX * Math.Sin(this.angle)
                        );

                        Canvas.SetLeft
                        (
                            this.designerItem,
                            Canvas.GetLeft(this.designerItem) + deltaX * Math.Cos(this.angle) +
                                (this.transformOrigin.X * deltaX * (1 - Math.Cos(this.angle)))
                        );

                        this.designerItem.Width -= deltaX;

                        break;

                    case HorizontalAlignment.Right :

                        deltaX = Math.Min(-e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth);

                        Canvas.SetTop
                        (
                            this.designerItem,
                            Canvas.GetTop(this.designerItem) - this.transformOrigin.X * deltaX * Math.Sin(this.angle)
                        );

                        Canvas.SetLeft
                        (
                            this.designerItem,
                            Canvas.GetLeft(this.designerItem) + (deltaX * this.transformOrigin.X * (1 - Math.Cos(this.angle)))
                        );

                        this.designerItem.Width -= deltaX;

                        break;

                    default :

                        break;
                }
            }

            e.Handled = true;
        }

        #endregion
    }
}

 

▶ RotateThumb.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 회전 썸
    /// </summary>
    public class RotateThumb : Thumb
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 중심 포인트
        /// </summary>
        private Point centerPoint;

        /// <summary>
        /// 시작 벡터
        /// </summary>
        private Vector startVector;

        /// <summary>
        /// 초기 각도
        /// </summary>
        private double initialAngle;

        /// <summary>
        /// 디자이너 캔버스
        /// </summary>
        private Canvas designerCanvas;

        /// <summary>
        /// 디자이너 항목
        /// </summary>
        private ContentControl designerItem;

        /// <summary>
        /// 회전 변환
        /// </summary>
        private RotateTransform rotateTransform;

        #endregion

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

        #region 생성자 - RotateThumb()

        /// <summary>
        /// 생성자
        /// </summary>
        public RotateThumb()
        {
            DragDelta   += Thumb_DragDelta;
            DragStarted += Thumb_DragStarted;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 썸 드래그 시작시 처리하기 - Thumb_DragStarted(sender, e)

        /// <summary>
        /// 썸 드래그 시작시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
        {
            this.designerItem = DataContext as ContentControl;

            if(this.designerItem != null)
            {
                this.designerCanvas = VisualTreeHelper.GetParent(this.designerItem) as Canvas;

                if(this.designerCanvas != null)
                {
                    this.centerPoint = this.designerItem.TranslatePoint
                    (
                        new Point
                        (
                            this.designerItem.Width  * this.designerItem.RenderTransformOrigin.X,
                            this.designerItem.Height * this.designerItem.RenderTransformOrigin.Y
                        ),
                        this.designerCanvas
                    );

                    Point startPoint = Mouse.GetPosition(this.designerCanvas);

                    this.startVector = Point.Subtract(startPoint, this.centerPoint);

                    this.rotateTransform = this.designerItem.RenderTransform as RotateTransform;

                    if(this.rotateTransform == null)
                    {
                        this.designerItem.RenderTransform = new RotateTransform(0);

                        this.initialAngle = 0;
                    }
                    else
                    {
                        this.initialAngle = this.rotateTransform.Angle;
                    }
                }
            }
        }

        #endregion
        #region 썸 드래그 델타 처리하기 - Thumb_DragDelta(sender, e)

        /// <summary>
        /// 썸 드래그 델타 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            if(this.designerItem != null && this.designerCanvas != null)
            {
                Point mousePoint = Mouse.GetPosition(this.designerCanvas);

                Vector deltaVector = Point.Subtract(mousePoint, this.centerPoint);

                double angle = Vector.AngleBetween(this.startVector, deltaVector);

                RotateTransform rotateTransform = this.designerItem.RenderTransform as RotateTransform;

                rotateTransform.Angle = this.initialAngle + Math.Round(angle, 0);

                this.designerItem.InvalidateMeasure();
            }
        }

        #endregion
    }
}

 

▶ MoveThumb.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestProject">
    <ControlTemplate x:Key="MoveThumbControlTemplateKey" TargetType="{x:Type local:MoveThumb}">
        <Rectangle Fill="Transparent" />
    </ControlTemplate>
</ResourceDictionary>

 

▶ ResizeDecorator.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestProject">
    <ControlTemplate x:Key="ResizeDecoratorControlTemplateKey" TargetType="{x:Type Control}">
        <Grid SnapsToDevicePixels="True" Opacity="1">
            <local:ResizeThumb
                HorizontalAlignment="Left"
                VerticalAlignment="Stretch"
                Margin="-4 0 0 0"
                Width="3"
                Cursor="SizeWE" />
            <local:ResizeThumb
                HorizontalAlignment="Stretch"
                VerticalAlignment="Top"
                Margin="0 -4 0 0"
                Height="3"
                Cursor="SizeNS" />
            <local:ResizeThumb
                HorizontalAlignment="Right"
                VerticalAlignment="Stretch"
                Margin="0 0 -4 0"
                Width="3"
                Cursor="SizeWE" />
            <local:ResizeThumb
                HorizontalAlignment="Stretch"
                VerticalAlignment="Bottom"
                Margin="0 0 0 -4"
                Height="3"
                Cursor="SizeNS" />

            <local:ResizeThumb
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Margin="-6 -6 0 0"
                Width="7"
                Height="7"
                Cursor="SizeNWSE" />
            <local:ResizeThumb
                HorizontalAlignment="Right"
                VerticalAlignment="Top"
                Margin="0 -6 -6 0"
                Width="7"
                Height="7"
                Cursor="SizeNESW" />
            <local:ResizeThumb
                HorizontalAlignment="Left"
                VerticalAlignment="Bottom"
                Margin="-6 0 0 -6"
                Width="7"
                Height="7"
                Cursor="SizeNESW" />
            <local:ResizeThumb
                HorizontalAlignment="Right"
                VerticalAlignment="Bottom"
                Margin="0 0 -6 -6"
                Width="7"
                Height="7"
                Cursor="SizeNWSE" />
        </Grid>
    </ControlTemplate>
</ResourceDictionary>

 

▶ RotateDecorator.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestProject">
    <Style TargetType="{x:Type local:RotateThumb}">
        <Setter Property="RenderTransformOrigin" Value="0.5 0.5" />
        <Setter Property="Cursor"                Value="Hand"    />
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:RotateThumb}">
                    <Grid Width="30" Height="30">
                        <Path
                            Stretch="Fill"
                            Fill="#AAD0D0DD"
                            Data="M 50 100 A 50 50 0 1 1 100 50 H 50 V 100" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <ControlTemplate x:Key="RotateDecoratorControlTemplateKey" TargetType="{x:Type Control}">
        <Grid>
            <local:RotateThumb
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Margin="-18 -18 0 0" />
            <local:RotateThumb
                HorizontalAlignment="Right"
                VerticalAlignment="Top"
                Margin="0 -18 -18 0">
                <local:RotateThumb.RenderTransform>
                    <RotateTransform Angle="90" />
                </local:RotateThumb.RenderTransform>
            </local:RotateThumb>
            <local:RotateThumb
                HorizontalAlignment="Right"
                VerticalAlignment="Bottom"
                Margin="0 0 -18 -18">
                <local:RotateThumb.RenderTransform>
                    <RotateTransform Angle="180" />
                </local:RotateThumb.RenderTransform>
            </local:RotateThumb>
            <local:RotateThumb
                Margin="-18 0 0 -18"
                VerticalAlignment="Bottom"
                HorizontalAlignment="Left">
                <local:RotateThumb.RenderTransform>
                    <RotateTransform Angle="270" />
                </local:RotateThumb.RenderTransform>
            </local:RotateThumb>
        </Grid>
    </ControlTemplate>
</ResourceDictionary>

 

▶ DesignerItem.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestProject">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="MoveThumb.xaml"       />
        <ResourceDictionary Source="ResizeDecorator.xaml" />
        <ResourceDictionary Source="RotateDecorator.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style x:Key="DesignerItemStyleKey" TargetType="ContentControl">
        <Setter Property="MinHeight"             Value="50"      />
        <Setter Property="MinWidth"              Value="50"      />    
        <Setter Property="RenderTransformOrigin" Value="0.5 0.5" />
        <Setter Property="SnapsToDevicePixels"   Value="true"    />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ContentControl">
                    <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                        <Control Name="RotateDecorator"
                            Template="{StaticResource RotateDecoratorControlTemplateKey}"
                            Visibility="Collapsed" />
                        <local:MoveThumb Template="{StaticResource MoveThumbControlTemplateKey}"
                            Cursor="SizeAll" />
                        <Control x:Name="ResizeDecorator"
                            Template="{StaticResource ResizeDecoratorControlTemplateKey}"
                            Visibility="Collapsed" />
                        <ContentPresenter Content="{TemplateBinding ContentControl.Content}" />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Selector.IsSelected" Value="True">
                            <Setter TargetName="ResizeDecorator" Property="Visibility" Value="Visible" />
                            <Setter TargetName="RotateDecorator" Property="Visibility" Value="Visible" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

 

▶ 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"
    WindowStartupLocation="CenterScreen"
    Width="800"
    Height="600"
    Title="Thumb 클래스 : 객체 이동하기/크기 조정하기/회전하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resource/DesignerItem.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Canvas>
        <ContentControl Canvas.Left="200" Canvas.Top="200"
            Style="{StaticResource DesignerItemStyleKey}"
            Width="150"
            Height="150"
            Selector.IsSelected="True">
            <Ellipse
                Fill="Blue"
                IsHitTestVisible="False" />
        </ContentControl>
        <ContentControl Canvas.Left="450" Canvas.Top="200"
            Style="{StaticResource DesignerItemStyleKey}"
            Width="150"
            Height="150"
            Selector.IsSelected="True">
            <Path
                Stretch="Fill"
                Fill="Red"
                IsHitTestVisible="False"
                Data="M 0 5, 5 0, 10 5, 5 10 Z" />
        </ContentControl>
    </Canvas>
</Window>

 

▶ MainWindow.xaml.cs

using System.Windows;

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

        #region 생성자 - MainWindow()

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

        #endregion
    }
}
728x90
반응형
그리드형
Posted by 사용자 icodebroker
TAG , ,

댓글을 달아 주세요