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

■ Adorner 클래스를 사용해 슬라이딩 어도너를 만드는 방법을 보여준다.

TestProject.zip
0.29MB

▶ UIElementAdorner.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// UI 엘리먼트 어도너
    /// </summary>
    public class UIElementAdorner : Adorner
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 자식 엘리먼트
        /// </summary>
        private readonly UIElement childElement;

        /// <summary>
        /// 왼쪽 오프셋
        /// </summary>
        private double leftOffset;

        /// <summary>
        /// 위쪽 오프셋
        /// </summary>
        private double topOffset;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 왼쪽 오프셋 - LeftOffset

        /// <summary>
        /// 왼쪽 오프셋
        /// </summary>
        public double LeftOffset
        {
            get
            {
                return this.leftOffset;
            }
            set
            {
                this.leftOffset = value;

                UpdateLocation();
            }
        }

        #endregion
        #region 위쪽 오프셋 - TopOffset

        /// <summary>
        /// 위쪽 오프셋
        /// </summary>
        public double TopOffset
        {
            get
            {
                return this.topOffset;
            }
            set
            {
                this.topOffset = value;

                UpdateLocation();
            }
        }

        #endregion

        #region 논리적 자식 열거자 - LogicalChildren

        /// <summary>
        /// 논리적 자식 열거자
        /// </summary>
        protected override IEnumerator LogicalChildren
        {
            get
            {
                List<UIElement> list = new List<UIElement> { this.childElement };

                return list.GetEnumerator();
            }
        }

        #endregion
        #region 비주얼 자식 수 - VisualChildrenCount

        /// <summary>
        /// 비주얼 자식 수
        /// </summary>
        protected override int VisualChildrenCount
        {
            get { return 1; }
        }

        #endregion

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

        #region 생성자 - UIElementAdorner(adornedElement, childElement)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="adornedElement">장식 엘리먼트</param>
        /// <param name="childElement">자식 엘리먼트</param>
        public UIElementAdorner(UIElement adornedElement, UIElement childElement) : base(adornedElement)
        {
            if(childElement == null)
            {
                throw new ArgumentNullException("childElement");
            }

            this.childElement = childElement;

            AddLogicalChild(childElement);
            AddVisualChild(childElement);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 희망 변환 구하기 - GetDesiredTransform(transform)

        /// <summary>
        /// 희망 변환 구하기
        /// </summary>
        /// <param name="transform">변환</param>
        /// <returns>희망 변환</returns>
        public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
        {
            GeneralTransformGroup transformGroup = new GeneralTransformGroup();

            transformGroup.Children.Add(base.GetDesiredTransform(transform));

            transformGroup.Children.Add(new TranslateTransform(this.leftOffset, this.topOffset));

            return transformGroup;
        }

        #endregion
        #region 오프셋 설정하기 - SetOffset(leftOffset, topOffset)

        /// <summary>
        /// 오프셋 설정하기
        /// </summary>
        /// <param name="leftOffset">왼쪽 오프셋</param>
        /// <param name="topOffset">위쪽 오프셋</param>
        public void SetOffset(double leftOffset, double topOffset)
        {
            this.leftOffset = leftOffset;
            this.topOffset  = topOffset;

            UpdateLocation();
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 측정하기 (오버라이드) - MeasureOverride(availableSize)

        /// <summary>
        /// 측정하기 (오버라이드)
        /// </summary>
        /// <param name="availableSize">희망 크기</param>
        /// <returns>크기</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            this.childElement.Measure(availableSize);

            return this.childElement.DesiredSize;
        }

        #endregion
        #region 배열하기 (오버라이드) - ArrangeOverride(finalSize)

        /// <summary>
        /// 배열하기 (오버라이드)
        /// </summary>
        /// <param name="finalSize">최종 크기</param>
        /// <returns>크기</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            this.childElement.Arrange(new Rect(finalSize));

            return finalSize;
        }

        #endregion
        #region 비주얼 자식 구하기 - GetVisualChild(index)

        /// <summary>
        /// 비주얼 자식 구하기
        /// </summary>
        /// <param name="index">인덱스</param>
        /// <returns>비주얼 자식</returns>
        protected override Visual GetVisualChild(int index)
        {
            return this.childElement;
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 위치 업데이트하기 - UpdateLocation()

        /// <summary>
        /// 위치 업데이트하기
        /// </summary>
        private void UpdateLocation()
        {
            AdornerLayer adornerLayer = Parent as AdornerLayer;

            if(adornerLayer != null)
            {
                adornerLayer.Update(AdornedElement);
            }
        }

        #endregion
    }
}

 

▶ SlidingElement.cs

using System.Windows.Controls;

namespace TestProject
{
    /// <summary>
    /// 슬라이딩 엘리먼트
    /// </summary>
    public class SlidingElement : Control
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - SlidingElement(dataContext)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="dataContext">데이터 컨텍스트</param>
        public SlidingElement(object dataContext)
        {
            DataContext = dataContext;
        }

        #endregion
    }
}

 

▶ SlidingAdorner.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace TestProject
{
    /// <summary>
    /// 슬라이딩 어도너
    /// </summary>
    public class SlidingAdorner : Control
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region SlidingAdornerSlideDirection

        /// <summary>
        /// 슬라이딩 어도너 슬라이드 방향
        /// </summary>
        public enum SlidingAdornerSlideDirection
        {
            /// <summary>
            /// 오른쪽
            /// </summary>
            Right,

            /// <summary>
            /// 왼쪽
            /// </summary>
            Left,

            /// <summary>
            /// 위쪽
            /// </summary>
            Up,

            /// <summary>
            /// 아래쪽
            /// </summary>
            Down
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 슬라이드 방향 속성 - SlideDirectionProperty

        /// <summary>
        /// 슬라이드 방향 속성
        /// </summary>
        public static readonly DependencyProperty SlideDirectionProperty = DependencyProperty.Register
        (
            "SlideDirection",
            typeof(SlidingAdornerSlideDirection),
            typeof(SlidingAdorner),
            new UIPropertyMetadata(SlidingAdornerSlideDirection.Right)
        );

        #endregion
        #region 슬라이드 거리 속성 - SlideDistanceProperty

        /// <summary>
        /// 슬라이드 거리 속성
        /// </summary>
        public static readonly DependencyProperty SlideDistanceProperty = DependencyProperty.Register
        (
            "SlideDistance",
            typeof(double),
            typeof(SlidingAdorner),
            new UIPropertyMetadata(100.0)
        );

        #endregion
        #region 슬라이드 지속 시간 속성 - SlideDurationProperty

        /// <summary>
        /// 슬라이드 지속 시간 속성
        /// </summary>
        public static readonly DependencyProperty SlideDurationProperty = DependencyProperty.Register
        (
            "SlideDuration",
            typeof(int),
            typeof(SlidingAdorner),
            new UIPropertyMetadata(500)
        );

        #endregion
        #region 슬라이드 엘리먼트 오프셋 속성 - SlideElementOffsetProperty

        /// <summary>
        /// 슬라이드 엘리먼트 오프셋 속성
        /// </summary>
        public static readonly DependencyProperty SlideElementOffsetProperty = DependencyProperty.Register
        (
            "SlideElementOffset",
            typeof(double),
            typeof(SlidingAdorner),
            new UIPropertyMetadata(0.0)
        );

        #endregion

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

        #region Field

        /// <summary>
        /// UI 엘리먼트 어도너
        /// </summary>
        private UIElementAdorner uiElementAdorner;

        /// <summary>
        /// 슬라이딩 엘리먼트
        /// </summary>
        private SlidingElement slidingElement;

        /// <summary>
        /// 슬라이딩 엘리먼트 표시 여부
        /// </summary>
        private bool isSlidingElementVisible;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 슬라이드 방향 - SlideDirection

        /// <summary>
        /// 슬라이드 방향
        /// </summary>
        public SlidingAdornerSlideDirection SlideDirection
        {
            get
            {
                return (SlidingAdornerSlideDirection)GetValue(SlideDirectionProperty);
            }
            set
            {
                SetValue(SlideDirectionProperty, value);
            }
        }

        #endregion
        #region 슬라이드 거리 - SlideDistance

        /// <summary>
        /// 슬라이드 거리
        /// </summary>
        public double SlideDistance
        {
            get
            {
                return (double)GetValue(SlideDistanceProperty);
            }
            set
            {
                SetValue(SlideDistanceProperty, value);
            }
        }

        #endregion
        #region 슬라이드 지속 시간 - SlideDuration

        /// <summary>
        /// 슬라이드 지속 시간
        /// </summary>
        public int SlideDuration
        {
            get
            {
                return (int)GetValue(SlideDurationProperty);
            }
            set
            {
                SetValue(SlideDurationProperty, value);
            }
        }

        #endregion
        #region 슬라이드 엘리먼트 오프셋 - SlideElementOffset

        /// <summary>
        /// 슬라이드 엘리먼트 오프셋
        /// </summary>
        public double SlideElementOffset
        {
            get
            {
                return (double)GetValue(SlideElementOffsetProperty);
            }
            set
            {
                SetValue(SlideElementOffsetProperty, value);
            }
        }

        #endregion

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

        #region 생성자 - SlidingAdorner()

        /// <summary>
        /// 생성자
        /// </summary>
        public SlidingAdorner()
        {
            MouseEnter += Control_MouseEnter;
            MouseLeave += Control_MouseLeave;
        }

        #endregion
        #region 생성자 - SlidingAdorner(dataContext)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="dataContext">데이터 컨텍스트</param>
        public SlidingAdorner(object dataContext)
        {
            DataContext = dataContext;

            MouseEnter += Control_MouseEnter;
            MouseLeave += Control_MouseLeave;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 변환 그룹 구하기 - GetTransformGroup(scaleX, scaleY, x, y)

        /// <summary>
        /// 변환 그룹 구하기
        /// </summary>
        /// <param name="scaleX">스케일 X</param>
        /// <param name="scaleY">스케일 Y</param>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <returns>변환 그룹</returns>
        private static TransformGroup GetTransformGroup(double scaleX, double scaleY, int x, int y)
        {
            TransformGroup transformGroup = new TransformGroup();

            ScaleTransform scaleTransform = new ScaleTransform { ScaleX = scaleX, ScaleY = scaleY };

            transformGroup.Children.Add(scaleTransform);

            TranslateTransform translateTransform = new TranslateTransform { X = x, Y = y };

            transformGroup.Children.Add(translateTransform);

            return transformGroup;
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 템플리트 적용시 처리하기 - OnApplyTemplate()

        /// <summary>
        /// 템플리트 적용시 처리하기
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.slidingElement = new SlidingElement(DataContext);

            this.slidingElement.MouseLeave += slidingElement_MouseLeave;

            this.slidingElement.RenderTransform = GetTransformGroup(1, 1, 1, 1);

            switch(SlideDirection)
            {
                case SlidingAdornerSlideDirection.Right :
                case SlidingAdornerSlideDirection.Left  :

                    this.slidingElement.Width = SlideDistance;

                    break;

                case SlidingAdornerSlideDirection.Down :
                case SlidingAdornerSlideDirection.Up   :

                    this.slidingElement.Height = SlideDistance;

                    break;
            }

            this.uiElementAdorner = new UIElementAdorner(this, this.slidingElement);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private
        ////////////////////////////////////////////////////////////////////// Event

        #region 컨트롤 마우스 진입시 처리하기 - Control_MouseEnter(sender, e)

        /// <summary>
        /// 컨트롤 마우스 진입시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Control_MouseEnter(object sender, MouseEventArgs e)
        {
            if(!this.isSlidingElementVisible)
            {
                ShowSlidingAdorner();
            }
        }

        #endregion
        #region 컨트롤 마우스 이탈시 처리하기 - Control_MouseLeave(sender, e)

        /// <summary>
        /// 컨트롤 마우스 이탈시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Control_MouseLeave(object sender, MouseEventArgs e)
        {
            ProcessMouseLeave();
        }

        #endregion
        #region 슬라이딩 엘리먼트 마우스 이탈시 처리하기 - slidingElement_MouseLeave(sender, e)

        /// <summary>
        /// 슬라이딩 엘리먼트 마우스 이탈시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void slidingElement_MouseLeave(object sender, MouseEventArgs e)
        {
            ProcessMouseLeave();
        }

        #endregion

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

        #region 슬라이딩 어도너 표시하기 - ShowSlidingAdorner()

        /// <summary>
        /// 슬라이딩 어도너 표시하기
        /// </summary>
        private void ShowSlidingAdorner()
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);

            if(adornerLayer == null)
            {
                return;
            }

            switch(SlideDirection)
            {
                case SlidingAdornerSlideDirection.Left :

                    this.uiElementAdorner.SetOffset(-SlideElementOffset, 0);

                    break;

                case SlidingAdornerSlideDirection.Right :

                    this.uiElementAdorner.SetOffset(ActualWidth + SlideElementOffset, 0);

                    break;

                case SlidingAdornerSlideDirection.Down :

                    this.uiElementAdorner.SetOffset(0, ActualHeight + SlideElementOffset);

                    break;

                case SlidingAdornerSlideDirection.Up :

                    this.uiElementAdorner.SetOffset(0, -SlideElementOffset);

                    break;
            }

            adornerLayer.Add(this.uiElementAdorner);

            AnimateScaleTransform();

            AnimateTranslateTransform();

            AnimateOpacity();
        }

        #endregion
        #region 스케일 변환 애니메이션 적용하기 - AnimateScaleTransform()

        /// <summary>
        /// 스케일 변환 애니메이션 적용하기
        /// </summary>
        private void AnimateScaleTransform()
        {
            SplineDoubleKeyFrame splineDoubleKeyFrame1 = new SplineDoubleKeyFrame
            {
                KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)),
                Value   = 0
            };

            SplineDoubleKeyFrame splineDoubleKeyFrame2 = new SplineDoubleKeyFrame
            {
                KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(SlideDuration)),
                Value   = 1
            };

            DoubleAnimationUsingKeyFrames doubleAnimationUsingKeyFrames = new DoubleAnimationUsingKeyFrames();

            doubleAnimationUsingKeyFrames.KeyFrames.Add(splineDoubleKeyFrame1);
            doubleAnimationUsingKeyFrames.KeyFrames.Add(splineDoubleKeyFrame2);

            ScaleTransform scaleTransform = ((TransformGroup)this.slidingElement.RenderTransform).Children[0] as ScaleTransform;

            if(scaleTransform != null)
            {
                switch(SlideDirection)
                {
                    case SlidingAdornerSlideDirection.Right :
                    case SlidingAdornerSlideDirection.Left  :

                        scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, doubleAnimationUsingKeyFrames);

                        break;

                    case SlidingAdornerSlideDirection.Down :
                    case SlidingAdornerSlideDirection.Up   :

                        scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, doubleAnimationUsingKeyFrames);

                        break;
                }
            }
        }

        #endregion
        #region 이동 변환 애니메이션 적용하기 - AnimateTranslateTransform()

        /// <summary>
        /// 이동 변환 애니메이션 적용하기
        /// </summary>
        private void AnimateTranslateTransform()
        {
            SplineDoubleKeyFrame splineDoubleKeyFrame1 = new SplineDoubleKeyFrame
            {
                KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)),
                Value   = 0
            };

            SplineDoubleKeyFrame splineDoubleKeyFrame2 = new SplineDoubleKeyFrame
            {
                KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(SlideDuration))
            };

            switch(SlideDirection)
            {
                case SlidingAdornerSlideDirection.Right :
                case SlidingAdornerSlideDirection.Down  :

                    splineDoubleKeyFrame2.Value = 0;

                    break;

                case SlidingAdornerSlideDirection.Left :
                case SlidingAdornerSlideDirection.Up   :

                    splineDoubleKeyFrame2.Value = -SlideDistance;

                    break;
            }

            DoubleAnimationUsingKeyFrames doubleAnimationUsingKeyFrames = new DoubleAnimationUsingKeyFrames();

            doubleAnimationUsingKeyFrames.KeyFrames.Add(splineDoubleKeyFrame1);
            doubleAnimationUsingKeyFrames.KeyFrames.Add(splineDoubleKeyFrame2);

            TranslateTransform translateTransform = ((TransformGroup)this.slidingElement.RenderTransform).Children[1] as TranslateTransform;

            if(translateTransform != null)
            {
                switch(SlideDirection)
                {
                    case SlidingAdornerSlideDirection.Right :
                    case SlidingAdornerSlideDirection.Left  :

                        translateTransform.BeginAnimation(TranslateTransform.XProperty, doubleAnimationUsingKeyFrames);

                        break;

                    case SlidingAdornerSlideDirection.Down :
                    case SlidingAdornerSlideDirection.Up   :

                        translateTransform.BeginAnimation(TranslateTransform.YProperty, doubleAnimationUsingKeyFrames);

                        break;
                }
            }
        }

        #endregion
        #region 불투명도 애니메이션 적용하기 - AnimateOpacity()

        /// <summary>
        /// 불투명도 애니메이션 적용하기
        /// </summary>
        private void AnimateOpacity()
        {
            DoubleAnimation doubleAnimation = new DoubleAnimation
            {
                Duration = new Duration(TimeSpan.FromMilliseconds(SlideDuration)),
                From     = 0,
                To       = 1
            };

            Storyboard storyboard = new Storyboard();

            storyboard.Children.Add(doubleAnimation);

            Storyboard.SetTarget(doubleAnimation, this.slidingElement);
            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(UIElement.Opacity)"));

            storyboard.Begin();
        }

        #endregion
        #region 슬라이딩 어도너 숨기기 - HideSlidingAdorner()

        /// <summary>
        /// 슬라이딩 어도너 숨기기
        /// </summary>
        private void HideSlidingAdorner()
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);

            if(adornerLayer != null)
            {
                adornerLayer.Remove(this.uiElementAdorner);
            }
        }

        #endregion
        #region 마우스 이탈시 처리하기 - ProcessMouseLeave()

        /// <summary>
        /// 마우스 이탈시 처리하기
        /// </summary>
        private void ProcessMouseLeave()
        {
            if(!IsMouseOver && !this.slidingElement.IsMouseOver)
            {
                HideSlidingAdorner();

                this.isSlidingElementVisible = false;
            }
            else
            {
                this.isSlidingElementVisible = true;
            }
        }

        #endregion
    }
}

 

▶ DragCanvas.cs

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

namespace TestProject
{
    /// <summary>
    /// 드래그 캔버스
    /// </summary>
    public class DragCanvas : Canvas
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 드래그 가능 여부 첨부 속성 - CanBeDraggedProperty

        /// <summary>
        /// 드래그 가능 여부 첨부 속성
        /// </summary>
        public static readonly DependencyProperty CanBeDraggedProperty = DependencyProperty.RegisterAttached
        (
            "CanBeDragged",
            typeof(bool),
            typeof(DragCanvas),
            new UIPropertyMetadata(true)
        );

        #endregion
        #region 드래그 허용 여부 속성 - AllowDraggingProperty

        /// <summary>
        /// 드래그 허용 여부 속성
        /// </summary>
        public static readonly DependencyProperty AllowDraggingProperty = DependencyProperty.Register
        (
            "AllowDragging",
            typeof(bool),
            typeof(DragCanvas),
            new PropertyMetadata(true)
        );

        #endregion
        #region 뷰 외부 드래그 허용 여부 속성 - AllowDragOutOfViewProperty

        /// <summary>
        /// 뷰 외부 드래그 허용 여부 속성
        /// </summary>
        public static readonly DependencyProperty AllowDragOutOfViewProperty = DependencyProperty.Register
        (
            "AllowDragOutOfView",
            typeof(bool),
            typeof(DragCanvas),
            new UIPropertyMetadata(false)
        );

        #endregion

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

        #region Field

        /// <summary>
        /// 드래그 엘리먼트
        /// </summary>
        private UIElement dragElement;

        /// <summary>
        /// 드래그 시작 포인트
        /// </summary>
        private Point dragStartPoint;

        /// <summary>
        /// 드래그 시작 오프셋 X
        /// </summary>
        private double dragStartOffsetX;

        /// <summary>
        /// 드래그 시작 오프셋 Y
        /// </summary>
        private double dragStartOffsetY;

        /// <summary>
        /// 수정 오프셋 X
        /// </summary>
        private bool modifyOffsetX;

        /// <summary>
        /// 수정 오프셋 Y
        /// </summary>
        private bool modifyOffsetY;

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

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 드래그 허용 여부 - AllowDragging

        /// <summary>
        /// 드래그 허용 여부
        /// </summary>
        public bool AllowDragging
        {
            get
            {
                return (bool)GetValue(AllowDraggingProperty);
            }
            set
            {
                SetValue(AllowDraggingProperty, value);
            }
        }

        #endregion
        #region 뷰 외부 드래그 허용 여부 - AllowDragOutOfView

        /// <summary>
        /// 뷰 외부 드래그 허용 여부
        /// </summary>
        public bool AllowDragOutOfView
        {
            get
            {
                return (bool)GetValue(AllowDragOutOfViewProperty);
            }
            set
            {
                SetValue(AllowDragOutOfViewProperty, value);
            }
        }

        #endregion
        #region 드래그 엘리먼트 - ElementBeingDragged

        /// <summary>
        /// 드래그 엘리먼트
        /// </summary>
        public UIElement ElementBeingDragged
        {
            get
            {
                return !AllowDragging ? null : this.dragElement;
            }
            protected set
            {
                if(this.dragElement != null)
                {
                    this.dragElement.ReleaseMouseCapture();
                }

                if(!AllowDragging)
                {
                    this.dragElement = null;
                }
                else
                {
                    if(GetCanBeDragged(value))
                    {
                        this.dragElement = value;

                        this.dragElement.CaptureMouse();
                    }
                    else
                    {
                        this.dragElement = null;
                    }
                }
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 드래그 가능 여부 구하기 - GetCanBeDragged(element)

        /// <summary>
        /// 드래그 가능 여부 구하기
        /// </summary>
        /// <param name="element">엘리먼트</param>
        /// <returns>드래그 가능 여부</returns>
        public static bool GetCanBeDragged(UIElement element)
        {
            if(element == null)
            {
                return false;
            }

            return (bool)element.GetValue(CanBeDraggedProperty);
        }

        #endregion
        #region 드래그 가능 여부 설정하기 - SetCanBeDragged(element, value)

        /// <summary>
        /// 드래그 가능 여부 설정하기
        /// </summary>
        /// <param name="element">엘리먼트</param>
        /// <param name="value">값</param>
        public static void SetCanBeDragged(UIElement element, bool value)
        {
            if(element != null)
            {
                element.SetValue(CanBeDraggedProperty, value);
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 전면으로 배치하기 - BringToFront(element)

        /// <summary>
        /// 전면으로 배치하기
        /// </summary>
        /// <param name="element">엘리먼트</param>
        public void BringToFront(UIElement element)
        {
            UpdateZOrder(element, true);
        }

        #endregion
        #region 후면으로 배치하기 - SendToBack(element)

        /// <summary>
        /// 후면으로 배치하기
        /// </summary>
        /// <param name="element">엘리먼트</param>
        public void SendToBack(UIElement element)
        {
            UpdateZOrder(element, false);
        }

        #endregion
        #region 캔버스 자식 찾기 - FindCanvasChild(dependencyObject)

        /// <summary>
        /// 캔버스 자식 찾기
        /// </summary>
        /// <param name="dependencyObject">의존 객체</param>
        /// <returns>캔버스 자식</returns>
        public UIElement FindCanvasChild(DependencyObject dependencyObject)
        {
            while(dependencyObject != null)
            {
                UIElement element = dependencyObject as UIElement;

                if(element != null && Children.Contains(element))
                {
                    break;
                }

                if(dependencyObject is Visual || dependencyObject is Visual3D)
                {
                    dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
                }
                else
                {
                    dependencyObject = LogicalTreeHelper.GetParent(dependencyObject);
                }
            }

            return dependencyObject as UIElement;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Protected

        #region PREVIEW 마우스 왼쪽 버튼 DOWN 처리하기 - OnPreviewMouseLeftButtonDown(e)

        /// <summary>
        /// PREVIEW 마우스 왼쪽 버튼 DOWN 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseLeftButtonDown(e);

            this.isDragging = false;

            this.dragStartPoint = e.GetPosition(this);

            ElementBeingDragged = FindCanvasChild(e.Source as DependencyObject);

            if(ElementBeingDragged == null)
            {
                return;
            }

            double left   = GetLeft  (ElementBeingDragged);
            double right  = GetRight (ElementBeingDragged);
            double top    = GetTop   (ElementBeingDragged);
            double bottom = GetBottom(ElementBeingDragged);

            this.dragStartOffsetX = ResolveOffset(left, right , out this.modifyOffsetX);
            this.dragStartOffsetY = ResolveOffset(top , bottom, out this.modifyOffsetY);

            e.Handled = true;

            this.isDragging = true;
        }

        #endregion
        #region PREVIEW 마우스 이동시 처리하기 - OnPreviewMouseMove(e)

        /// <summary>
        /// PREVIEW 마우스 이동시 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            base.OnPreviewMouseMove(e);

            if(ElementBeingDragged == null || !this.isDragging)
            {
                return;
            }

            Point cursorPoint = e.GetPosition(this);

            double newOffsetX;
            double newOffsetY;

            if(this.modifyOffsetX)
            {
                newOffsetX = this.dragStartOffsetX + (cursorPoint.X - this.dragStartPoint.X);
            }
            else
            {
                newOffsetX = this.dragStartOffsetX - (cursorPoint.X - this.dragStartPoint.X);
            }

            if(this.modifyOffsetY)
            {
                newOffsetY = this.dragStartOffsetY + (cursorPoint.Y - this.dragStartPoint.Y);
            }
            else
            {
                newOffsetY = this.dragStartOffsetY - (cursorPoint.Y - this.dragStartPoint.Y);
            }

            if(!AllowDragOutOfView)
            {
                Rect elementRectangle = CalculateDragElementRectangle(newOffsetX, newOffsetY);

                bool alignLeft  = elementRectangle.Left < 0;
                bool alignRight = elementRectangle.Right > ActualWidth;

                if(alignLeft)
                {
                    newOffsetX = this.modifyOffsetX ? 0 : ActualWidth - elementRectangle.Width;
                }
                else if(alignRight)
                {
                    newOffsetX = this.modifyOffsetX ? ActualWidth - elementRectangle.Width : 0;
                }

                bool alignTop    = elementRectangle.Top < 0;
                bool alignBottom = elementRectangle.Bottom > ActualHeight;

                if(alignTop)
                {
                    newOffsetY = this.modifyOffsetY ? 0 : ActualHeight - elementRectangle.Height;
                }
                else if(alignBottom)
                {
                    newOffsetY = this.modifyOffsetY ? ActualHeight - elementRectangle.Height : 0;
                }
            }

            if(this.modifyOffsetX)
            {
                SetLeft(ElementBeingDragged, newOffsetX);
            }
            else
            {
                SetRight(ElementBeingDragged, newOffsetX);
            }

            if(this.modifyOffsetY)
            {
                SetTop(ElementBeingDragged, newOffsetY);
            }
            else
            {
                SetBottom(ElementBeingDragged, newOffsetY);
            }
        }

        #endregion
        #region PREVIEW 마우스 UP 처리하기 - OnPreviewMouseUp(e)

        /// <summary>
        /// PREVIEW 마우스 UP 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseUp(e);

            ElementBeingDragged = null;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 드래그 엘리먼트 사각형 계산하기 - CalculateDragElementRectangle(newOffsetX, newOffsetY)

        /// <summary>
        /// 드래그 엘리먼트 사각형 계산하기
        /// </summary>
        /// <param name="newOffsetX">신규 오프셋 X</param>
        /// <param name="newOffsetY">신규 오프셋 Y</param>
        /// <returns>드래그 엘리먼트 사각형</returns>
        private Rect CalculateDragElementRectangle(double newOffsetX, double newOffsetY)
        {
            if(ElementBeingDragged == null)
            {
                throw new InvalidOperationException("ElementBeingDragged is null.");
            }

            Size elementSize = ElementBeingDragged.RenderSize;

            double x;
            double y;

            if(this.modifyOffsetX)
            {
                x = newOffsetX;
            }
            else
            {
                x = ActualWidth - newOffsetX - elementSize.Width;
            }

            if(this.modifyOffsetY)
            {
                y = newOffsetY;
            }
            else
            {
                y = ActualHeight - newOffsetY - elementSize.Height;
            }

            Point elementPoint = new Point(x, y);

            return new Rect(elementPoint, elementSize);
        }

        #endregion
        #region 오프셋 결정하기 - ResolveOffset(side1, side2, useSide1)

        /// <summary>
        /// 오프셋 결정하기
        /// </summary>
        /// <param name="side1">측면 1</param>
        /// <param name="side2">측면 2</param>
        /// <param name="useSide1">측면 1 사용 여부</param>
        /// <returns>오프셋</returns>
        private static double ResolveOffset(double side1, double side2, out bool useSide1)
        {
            useSide1 = true;

            double result;

            if(double.IsNaN(side1))
            {
                if(double.IsNaN(side2))
                {
                    result = 0;
                }
                else
                {
                    result = side2;

                    useSide1 = false;
                }
            }
            else
            {
                result = side1;
            }

            return result;
        }

        #endregion
        #region Z 순서 업데이트하기 - UpdateZOrder(element, bringToFront)

        /// <summary>
        /// Z 순서 업데이트하기
        /// </summary>
        /// <param name="element">엘리먼트</param>
        /// <param name="bringToFront">전면 배치 여부</param>
        private void UpdateZOrder(UIElement element, bool bringToFront)
        {
            if(element == null)
            {
                return;
            }

            if(!Children.Contains(element))
            {
                throw new ArgumentException("Must be a child element of the Canvas.", "element");
            }

            int elementNewZIndex = -1;

            if(bringToFront)
            {
                foreach(UIElement childElement in Children)
                {
                    if(childElement.Visibility != Visibility.Collapsed)
                    {
                        ++elementNewZIndex;
                    }
                }
            }
            else
            {
                elementNewZIndex = 0;
            }

            int offset = (elementNewZIndex == 0) ? +1 : -1;

            int elementCurrentZIndex = GetZIndex(element);

            foreach(UIElement childElement in Children)
            {
                if(childElement == element)
                {
                    SetZIndex(element, elementNewZIndex);
                }
                else
                {
                    int zIndex = GetZIndex(childElement);

                    if(bringToFront && elementCurrentZIndex < zIndex || !bringToFront && zIndex < elementCurrentZIndex)
                    {
                        SetZIndex(childElement, zIndex + offset);
                    }
                }
            }
        }

        #endregion
    }
}

 

▶ Country.cs

using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 국가
    /// </summary>
    public class Country
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 국가명 - CountryName

        /// <summary>
        /// 국가명
        /// </summary>
        public string CountryName { get; set; }

        #endregion
        #region 국가 이미지 - CountryImage

        /// <summary>
        /// 국가 이미지
        /// </summary>
        public ImageSource CountryImage { get; set; }

        #endregion
        #region 국가 수도 - CountryCapital

        /// <summary>
        /// 국가 수도
        /// </summary>
        public string CountryCapital { get; set; }

        #endregion
        #region 국가 색상 - CountryColor

        /// <summary>
        /// 국가 색상
        /// </summary>
        public Color CountryColor { get; set; }

        #endregion
    }
}

 

▶ CountryHelper.cs

using System;
using System.Collections.Generic;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 국가 헬퍼
    /// </summary>
    public class CountryHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 국가 리스트 구하기 - GetCountryList()

        /// <summary>
        /// 국가 리스트 구하기
        /// </summary>
        /// <returns>국가 리스트</returns>
        public static List<Country> GetCountryList()
        {
            List<Country> list = new List<Country>
            {
                new Country
                {
                    CountryName    = "아르헨티나",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.ARGENTINA),
                    CountryCapital = "부에노스 아이레스",
                    CountryColor   = Color.FromRgb(82, 179, 255)
                },
               new Country
                {
                    CountryName    = "호주",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.AUSTRALIA),
                    CountryCapital = "캔버라",
                    CountryColor   = Color.FromRgb(0, 129, 231)
                },
                new Country
                {
                    CountryName    = "브라질",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.BRAZIL),
                    CountryCapital = "브라질리아",
                    CountryColor   = Color.FromRgb(255, 250, 0)
                },
               new Country
                {
                    CountryName    = "캐나다",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.CANADA),
                    CountryCapital = "오타와",
                    CountryColor   = Color.FromRgb(255, 99, 51)
                },
               new Country
                {
                    CountryName    = "중국",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.CHINA),
                    CountryCapital = "베이징",
                    CountryColor   = Color.FromRgb(255, 99, 51)
                },
               new Country
                {
                    CountryName    = "콜롬비아",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.COLOMBIA),
                    CountryCapital = "보고타",
                    CountryColor   = Color.FromRgb(255, 250, 0)
                },
               new Country
                {
                    CountryName    = "이집트",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.EGYPT),
                    CountryCapital = "카이로",
                    CountryColor   = Color.FromRgb(38, 38, 38)
                },
               new Country
                {
                    CountryName    = "영국",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.ENGLAND),
                    CountryCapital = "런던",
                    CountryColor   = Color.FromRgb(255, 99, 51)
                },
               new Country
                {
                    CountryName    = "핀란드",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.FINLAND),
                    CountryCapital = "헬싱키",
                    CountryColor   = Color.FromRgb(0, 140, 242)
                },
               new Country
                {
                    CountryName    = "독입",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.GERMANY),
                    CountryCapital = "베를린",
                    CountryColor   = Color.FromRgb(255, 58, 6)
                },
                new Country
                {
                    CountryName    = "인도",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.INDIA),
                    CountryCapital = "뉴 델리",
                    CountryColor   = Color.FromRgb(255, 195, 93)
                },
                new Country
                {
                    CountryName    = "케냐",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.KENYA),
                    CountryCapital = "나이로비",
                    CountryColor   = Color.FromRgb(38, 38, 38)
                },
                new Country
                {
                    CountryName    = "멕시코",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.MEXICO),
                    CountryCapital = "멕시코 시티",
                    CountryColor   = Color.FromRgb(32, 168, 15)
                },
                new Country
                {
                    CountryName    = "뉴질랜드",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.NEW_ZEALAND),
                    CountryCapital = "웰링톤",
                    CountryColor   = Color.FromRgb(0, 129, 231)
                },
                new Country
                {
                    CountryName    = "나이제리아",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.NIGERIA),
                    CountryCapital = "아부자",
                    CountryColor   = Color.FromRgb(32, 168, 15)
                },
                new Country
                {
                    CountryName    = "파키스탄",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.PAKISTAN),
                    CountryCapital = "이슬라마바드",
                    CountryColor   = Color.FromRgb(32, 168, 15)
                },
                new Country
                {
                    CountryName    = "필리핀",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.PHILIPPINES),
                    CountryCapital = "마닐라",
                    CountryColor   = Color.FromRgb(0, 123, 225)
                },
                new Country
                {
                    CountryName    = "러시아",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.RUSSIA),
                    CountryCapital = "모스크바",
                    CountryColor   = Color.FromRgb(0, 123, 225)
                },
                new Country
                {
                    CountryName    = "남아프리카 공화국",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.SOUTH_AFRICA),
                    CountryCapital = "케이프 타운",
                    CountryColor   = Color.FromRgb(52, 178, 25)
                },
               new Country
                {
                    CountryName    = "미국",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.USA),
                    CountryCapital = "워싱턴",
                    CountryColor   = Color.FromRgb(0, 123, 225)
                },
               new Country
                {
                    CountryName    = "베트남",
                    CountryImage   = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.VIETNAM),
                    CountryCapital = "하노이",
                    CountryColor   = Color.FromRgb(255, 99, 51)
                }
           };

            return GetRandomList(list, 10);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 임의 리스트 구하기 - GetRandomList<T>(itemCount, sourceList)

        /// <summary>
        /// 임의 리스트 구하기
        /// </summary>
        /// <typeparam name="TItem">항목 타입</typeparam>
        /// <param name="sourceList">소스 리스트</param>
        /// <param name="itemCount">항목 수</param>
        /// <returns>임의 리스트</returns>
        private static List<TItem> GetRandomList<TItem>(List<TItem> sourceList, int itemCount)
        {
            if(itemCount > sourceList.Count)
            {
                itemCount = sourceList.Count;
            }

            List<TItem> list = new List<TItem>(itemCount);

            for(var i = 0; i < itemCount; i++)
            {
                int index = new Random().Next(sourceList.Count);

                TItem item = sourceList[index];

                sourceList.RemoveAt(index);

                list.Add(item);
            }

            return list;
        }

        #endregion
    }
}

 

▶ ImageConstant.cs

namespace TestProject
{
    /// <summary>
    /// 이미지 상수
    /// </summary>
    public class ImageConstant
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 아르헨티나
        /// </summary>
        public const string ARGENTINA = "pack://application:,,,/TestProject;component/IMAGE/flag_argentina.png";

        /// <summary>
        /// 호주
        /// </summary>
        public const string AUSTRALIA = "pack://application:,,,/TestProject;component/IMAGE/flag_australia.png";

        /// <summary>
        /// 브라질
        /// </summary>
        public const string BRAZIL = "pack://application:,,,/TestProject;component/IMAGE/flag_brazil.png";

        /// <summary>
        /// 캐나다
        /// </summary>
        public const string CANADA = "pack://application:,,,/TestProject;component/IMAGE/flag_canada.png";

        /// <summary>
        /// 중국
        /// </summary>
        public const string CHINA = "pack://application:,,,/TestProject;component/IMAGE/flag_china.png";

        /// <summary>
        /// 콜롬비아
        /// </summary>
        public const string COLOMBIA = "pack://application:,,,/TestProject;component/IMAGE/flag_colombia.png";

        /// <summary>
        /// 이집트
        /// </summary>
        public const string EGYPT = "pack://application:,,,/TestProject;component/IMAGE/flag_egypt.png";

        /// <summary>
        /// 영국
        /// </summary>
        public const string ENGLAND = "pack://application:,,,/TestProject;component/IMAGE/flag_england.png";

        /// <summary>
        /// 핀란드
        /// </summary>
        public const string FINLAND = "pack://application:,,,/TestProject;component/IMAGE/flag_finland.png";

        /// <summary>
        /// 독일
        /// </summary>
        public const string GERMANY = "pack://application:,,,/TestProject;component/IMAGE/flag_germany.png";

        /// <summary>
        /// 인디아
        /// </summary>
        public const string INDIA = "pack://application:,,,/TestProject;component/IMAGE/flag_india.png";

        /// <summary>
        /// 케냐
        /// </summary>
        public const string KENYA = "pack://application:,,,/TestProject;component/IMAGE/flag_kenya.png";

        /// <summary>
        /// 멕시코
        /// </summary>
        public const string MEXICO = "pack://application:,,,/TestProject;component/IMAGE/flag_mexico.png";

        /// <summary>
        /// 뉴질랜드
        /// </summary>
        public const string NEW_ZEALAND = "pack://application:,,,/TestProject;component/IMAGE/flag_new_zealand.png";

        /// <summary>
        /// 나이제리아
        /// </summary>
        public const string NIGERIA = "pack://application:,,,/TestProject;component/IMAGE/flag_nigeria.png";

        /// <summary>
        /// 파키스탄
        /// </summary>
        public const string PAKISTAN = "pack://application:,,,/TestProject;component/IMAGE/flag_pakistan.png";

        /// <summary>
        /// 필리핀
        /// </summary>
        public const string PHILIPPINES = "pack://application:,,,/TestProject;component/IMAGE/flag_philippines.png";

        /// <summary>
        /// 러시아
        /// </summary>
        public const string RUSSIA = "pack://application:,,,/TestProject;component/IMAGE/flag_russia.png";

        /// <summary>
        /// 남아프리카 공화국
        /// </summary>
        public const string SOUTH_AFRICA = "pack://application:,,,/TestProject;component/IMAGE/flag_south_africa.png";

        /// <summary>
        /// 미국
        /// </summary>
        public const string USA = "pack://application:,,,/TestProject;component/IMAGE/flag_usa.png";

        /// <summary>
        /// 베트남
        /// </summary>
        public const string VIETNAM = "pack://application:,,,/TestProject;component/IMAGE/flag_vietnam.png";

        /// <summary>
        /// 세계 맵
        /// </summary>
        public const string WORLD_MAP = "pack://application:,,,/TestProject;component/IMAGE/world_map.jpg";

        #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"
    xmlns:local="clr-namespace:TestProject"
    Width="800"
    Height="600"
    Title="Adorner 클래스 : 슬라이딩 어도너 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Window.Resources>
        <Style TargetType="{x:Type local:SlidingAdorner}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:SlidingAdorner}">
                        <StackPanel Orientation="Vertical">
                            <Button>
                                <Button.Template>
                                    <ControlTemplate TargetType="Button">
                                        <Image
                                            Width="48"
                                            Height="48"
                                            Source="{Binding Path=CountryImage}" />
                                    </ControlTemplate>
                                </Button.Template>
                            </Button>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="{x:Type local:SlidingElement}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:SlidingElement}">
                        <StackPanel>
                            <Border CornerRadius="4">
                                <Border.Background>
                                    <LinearGradientBrush Opacity="0.8">
                                        <GradientStop Offset="0.0" Color="Transparent"                 />
                                        <GradientStop Offset="1.0" Color="{Binding Path=CountryColor}" />
                                    </LinearGradientBrush>
                                </Border.Background>
                                <StackPanel Orientation="Vertical">
                                    <Label
                                        Foreground="White"
                                        FontWeight="Bold"
                                        Content="{Binding Path=CountryName}" />
                                    <Label
                                        Foreground="White"
                                        Content="{Binding Path=CountryCapital}" />
                                </StackPanel>
                            </Border>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"    />
        </Grid.RowDefinitions>
        <Button Name="startButton" Grid.Row="0"
            Height="30"
            Content="새로 시작하기">
        </Button>
        <local:DragCanvas x:Name="dragCanvas" Grid.Row="1">
            <Canvas.Background>
                <ImageBrush
                    Stretch="Fill"
                    ImageSource="{Binding Source={x:Static local:ImageConstant.WORLD_MAP}}" />
            </Canvas.Background>
        </local:DragCanvas>
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

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

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

        #region 국가 리스트 - CountryList

        /// <summary>
        /// 국가 리스트
        /// </summary>
        public List<Country> CountryList { get; private set; }

        #endregion

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

        #region 생성자 - MainWindow()

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

            DataContext = this;

            Loaded                     += Window_Loaded;
            PreviewMouseLeftButtonDown += Window_PreviewMouseLeftButtonDown;
            this.startButton.Click     += startButton_Click;
        }

        #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.dragCanvas.Children.Clear();

            CountryList = CountryHelper.GetCountryList();

            SetDragCanvasData();
        }

        #endregion
        #region 윈도우 PREVIEW 마우스 왼쪽 버튼 DOWN 처리하기 - Window_PreviewMouseLeftButtonDown(sender, e)

        /// <summary>
        /// 윈도우 PREVIEW 마우스 왼쪽 버튼 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            UIElement childElement = this.dragCanvas.FindCanvasChild(e.Source as DependencyObject);

            this.dragCanvas.BringToFront(childElement);
        }

        #endregion
        #region 새로 시작하기 버튼 클릭시 처리하기 - startButton_Click(sender, e)

        /// <summary>
        /// 새로 시작하기 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void startButton_Click(object sender, RoutedEventArgs e)
        {
            this.dragCanvas.Children.Clear();

            CountryList = CountryHelper.GetCountryList();

            SetDragCanvasData();
        }

        #endregion

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

        #region 드래그 캔버스 데이터 설정하기 - SetDragCanvasData()

        /// <summary>
        /// 드래그 캔버스 데이터 설정하기
        /// </summary>
        private void SetDragCanvasData()
        {
            Random random = new Random();

            foreach(Country country in CountryList)
            {
                SlidingAdorner slidingAdorner = new SlidingAdorner(country);

                slidingAdorner.SetValue(Canvas.LeftProperty, Convert.ToDouble(random.Next(Convert.ToInt32(this.dragCanvas.ActualWidth ))));
                slidingAdorner.SetValue(Canvas.TopProperty , Convert.ToDouble(random.Next(Convert.ToInt32(this.dragCanvas.ActualHeight))));

                this.dragCanvas.Children.Add(slidingAdorner);
            }
        }

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

댓글을 달아 주세요