티스토리 뷰


WPF에는 Canvas WrapPanel, Grid 등과 같은 패널들이 존재하죠 ~

 

이러한 패널들을 상속받아 저만의 패널을 만들어 보는 방법을 소개합니다 ^^

 

Fish Eye라고 하면.. 물고기 눈이죠~

 

만들고자 했던 패널은

 

마우스 움직임을 감지하여 마우스 주변의 아이템들의 사이즈를 늘리는 패널입니다.

 

WrapPanel을 상속받아 사용해 보았습니다.

 

 

나름 상세한 주석을 달아놓았으니 부연설명은 필요 없을것입니다.


 

using System;

using System.Collections.Generic;

using System.Windows.Controls;

using System.Windows;

using System.Windows.Media;

using System.Windows.Input;

 

namespace PanelTest

{

    class FishEyePanel : WrapPanel

    {

        List<FrameworkElement> childList;   // 아이템들의 ZIndex를 조절하기 위해 만든 리스트

        double ItemLength = -1; // 아이템(Chilren객체의 대각선 길이)

        double maxArrange = 2;    // 아이템에 마우스가 다가갔을 때의 Max크기(기본값: 2)

 

        public double MaxArrange

        {

            set { maxArrange = value; }

        }

 

 

        #region EventHandler

        /// <summary>

        /// MouseMove 이벤트 핸들러

        /// </summary>

        /// <param name="e"></param>

        protected override void OnMouseMove(MouseEventArgs e)

        {

            AnimateChilds();

            base.OnMouseMove(e);

        }

 

        /// <summary>

        /// MouseLeave 이벤트 핸들러

        /// </summary>

        /// <param name="e"></param>

        protected override void OnMouseLeave(MouseEventArgs e)

        {

            foreach (UIElement child in Children)

            {

                FrameworkElement content = child as FrameworkElement;

                ScaleAnimate(content.RenderTransform as ScaleTransform, 1, 1);

            }

            base.OnMouseLeave(e);

        }

        #endregion

 

        #region Methods

        /// <summary>

        /// Children 객체들의 크기 조정

        /// Children 객체들의 ZIndex 조정

        /// </summary>

        private void AnimateChilds()

        {

            if (childList == null) childList = new List<FrameworkElement>();

           

            childList.Clear();

 

            foreach (UIElement child in Children)

            {

                FrameworkElement content = child as FrameworkElement;

 

                //

                // 크기를 조정하기 위해 아이템의 대각선 길이를 가져옴

                //

                if (ItemLength == -1) ItemLength = GetLength(content.Width, content.Height);

 

                //

                // ScaleTransform이 지정되어있지 않다면 생성

                // RenderTransform이 아닌 LayoutTransform으로 할 경우 애니메이션에 해당하지 않는 객체들까지 움직이게 할 수 있음

                //

                if (content.RenderTransform as ScaleTransform == null)

                {

                    content.RenderTransform = new ScaleTransform(1, 1, content.Width / 2, content.Height / 2);

                }

 

                //

                // 현재 마우스 위치와 아이템간의 거리를 구하고 아이템 크기의 maxScale배수 이하일 때 크기를 조정

                //

                double length = GetLength(Mouse.GetPosition(content).X - (content.Width / 2),  Mouse.GetPosition(content).Y - (content.Height / 2));

                length = maxArrange - (length / (maxArrange * ItemLength));

                if (length < 1) length = 1;

               

                //

                // 크기 조정

                //

                ScaleTransform st = content.RenderTransform as ScaleTransform;

                ScaleAnimate(st, length, length);

               

                //

                // ZIndex를 조절하기 위해 리스트에 추가

                //

                childList.Add(content);

            }

 

            //

            // ZIndex를 조절하기 위해 메소드 호출

            //

            SetChildZIndex();

        }

 

        /// <summary>

        /// 아이템들의 ZIndex를 조절

        /// 마우스에 가까운 아이템이 상단에 올라오도록

        /// </summary>

        private void SetChildZIndex()

        {

            //

            // ScaleTransform ScaleX를 가지고 정렬

            //

            childList.Sort(new CompScale());

 

            //

            // Scale이 클수록 ZIndex에 큰 값이 들어감

            //

            for (int i = 0; i < childList.Count; i++)

            {

                Panel.SetZIndex(childList[i], i);

            }

        }

 

        /// <summary>

        /// 거리를 재기 위해 만든 메소드

        /// </summary>

        /// <param name="x">x포지션</param>

        /// <param name="y">y포지션</param>

        /// <returns>(0, 0)부터 (x, y)좌표 까지의 길이</returns>

        private double GetLength(double x, double y)

        {

            return Math.Sqrt(x * x + y * y );

        }

 

        /// <summary>

        /// ScaleTransform ScaleX ScaleY를 셋팅

        /// </summary>

        /// <param name="st">ScaleTranfrom객체</param>

        /// <param name="scaleX">X Scale(배수)</param>

        /// <param name="scaleY">Y Scale(배수)</param>

        private void ScaleAnimate(ScaleTransform st, double scaleX, double scaleY)

        {

            st.ScaleX = scaleX;

            st.ScaleY = scaleY;

        }

        #endregion

    }

 

    /// <summary>

    /// ScaleTransform ScaleX를 이용해 리스트를 정렬시키기 위해 IComparer를 구현한 클래스

    /// </summary>

    class CompScale : IComparer<FrameworkElement>

    {

        #region IComparer<FrameworkElement> 멤버

 

        public int Compare(FrameworkElement x, FrameworkElement y)

        {

            return (x.RenderTransform as ScaleTransform).ScaleX.CompareTo((y.RenderTransform as ScaleTransform).ScaleX);

        }

 

        #endregion

    }

}

 

 

FishEyePanel이라는 클래스를 WrapPanel을 상속받아 만들어보았습니다.

 

사용 예제입니다.

 

 

=== xaml

<Window x:Class="PanelTest.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="clr-namespace:PanelTest"

    Title="Window1" Height="300" Width="300">

    <local:FishEyePanel x:Name="LayoutRoot" />

</Window>

 

== 코드비하인드

public Window1()

{

    InitializeComponent();

    this.Loaded += new RoutedEventHandler(Window1_Loaded);

}

 

void Window1_Loaded(object sender, RoutedEventArgs e)

{

    LayoutRoot.MaxArrange = 2.5; // 이건 옵션

    for (int i = 0; i < 400; i++)

    {

        Rectangle rect = new Rectangle();

        rect.Fill = new SolidColorBrush(Color.FromArgb(30, 0, 125, 255));

        rect.Stroke = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0));

        rect.Margin = new Thickness(5);

        rect.Width = 50;

        rect.Height = 50;

        LayoutRoot.Children.Add(rect);

    }

}

 

 

 

반응형
댓글