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

TestProject.zip
다운로드

▶ FrameworkElementExtention.cs

using System.Windows;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 프레임워크 엘리먼트 확장
    /// </summary>
    public static class FrameworkElementExtention
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 회전하기 - Rotate(source, angle)

        /// <summary>
        /// 회전하기
        /// </summary>
        /// <param name="source">소스 엘리먼트</param>
        /// <param name="angle">각도</param>
        public static void Rotate(this FrameworkElement source, double angle)
        {
            source.RenderTransformOrigin = new Point(0.5, 1);

            source.HorizontalAlignment = HorizontalAlignment.Center;
            source.VerticalAlignment   = VerticalAlignment.Center;

            if(source.RenderTransform is RotateTransform)
            {
                (source.RenderTransform as RotateTransform).Angle = angle;
            }
            else
            {
                source.RenderTransform = new RotateTransform(angle);
            }
        }

        #endregion
    }
}

 

728x90

 

▶ CircularGrid.cs

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

namespace TestProject
{
    /// <summary>
    /// 환형 그리드
    /// </summary>
    public class CircularGrid : Grid
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 회전 변환 - RotateTransform

        /// <summary>
        /// 회전 변환
        /// </summary>
        public RotateTransform RotateTransform { get; private set; }

        #endregion
        #region 이동 변환 - TranslateTransform

        /// <summary>
        /// 이동 변환
        /// </summary>
        public TranslateTransform TranslateTransform { get; private set; }

        #endregion
        #region 항목 각도 - ItemAngle

        /// <summary>
        /// 항목 각도
        /// </summary>
        public double ItemAngle { get; private set; }

        #endregion
        #region 항목 너비 - ItemWidth

        /// <summary>
        /// 항목 너비
        /// </summary>
        public double ItemWidth { get; private set; }

        #endregion
        #region 항목 높이 - ItemHeight

        /// <summary>
        /// 항목 
        /// </summary>
        public double ItemHeight { get; private set; }

        #endregion

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

        #region 생성자 - CircularGrid()

        /// <summary>
        /// 생성자
        /// </summary>
        public CircularGrid()
        {
            RenderTransformOrigin = new Point(0.5, 0.5);

            TransformGroup transformGroup = new TransformGroup();

            RotateTransform    = new RotateTransform();
            TranslateTransform = new TranslateTransform();
            
            transformGroup.Children.Add(RotateTransform   );
            transformGroup.Children.Add(TranslateTransform);

            RenderTransform = transformGroup;

            PreviewMouseWheel += Grid_PreviewMouseWheel;
        }

        #endregion

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

        #region 그리드 프리뷰 마우스 DOWN 처리하기 - Grid_PreviewMouseDown(sender, e)

        /// <summary>
        /// 그리드 프리뷰 마우스 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement selectedItem = sender as FrameworkElement;

            double currentAngle     = RotateTransform.Angle % 360 ;
            int    itemCount        = Children.Count;
            int    currentIndex     = itemCount - (int)(currentAngle / ItemAngle);
            int    selectedIndex    = Children.IndexOf(selectedItem);
            int    distance         = selectedIndex - currentIndex;
            int    absoluteDistance = Math.Abs(distance);
            double rotateAngle      = 0;

            if(distance > 0)
            {
                if(absoluteDistance > itemCount / 2)
                {
                    rotateAngle = (itemCount - absoluteDistance) * ItemAngle;
                }
                else
                {
                    rotateAngle = -(absoluteDistance * ItemAngle);
                }
            }
            else
            {
                if(absoluteDistance > itemCount / 2)
                {
                    rotateAngle = -(itemCount - absoluteDistance) * ItemAngle;
                }
                else
                {
                    rotateAngle = absoluteDistance * ItemAngle;
                }
            }

            DoubleAnimation angleAnimation = new DoubleAnimation()
            {
                SpeedRatio        = 2,
                AccelerationRatio = 0.7
            };

            angleAnimation.By = rotateAngle;

            RotateTransform.BeginAnimation(RotateTransform.AngleProperty, angleAnimation, HandoffBehavior.Compose);
        }

        #endregion
        #region 그리드 프리뷰 마우스 휠 처리하기 - Grid_PreviewMouseWheel(sender, e)

        /// <summary>
        /// 그리드 프리뷰 마우스 휠 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Grid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            DoubleAnimation angleAnimation = new DoubleAnimation()
            {
                SpeedRatio        = 2,
                AccelerationRatio = 0.7
            };

            angleAnimation.By = (e.Delta > 0 ? -ItemAngle : ItemAngle);

            RotateTransform.BeginAnimation(RotateTransform.AngleProperty, angleAnimation, HandoffBehavior.Compose);
        }

        #endregion

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

        #region 비주얼 자식 컬렉션 변경시 처리하기 - OnVisualChildrenChanged(addedObject, removedObject)

        /// <summary>
        /// 비주얼 자식 컬렉션 변경시 처리하기
        /// </summary>
        /// <param name="addedObject">추가 객체</param>
        /// <param name="removedObject">제거 객체</param>
        protected override void OnVisualChildrenChanged(DependencyObject addedObject, DependencyObject removedObject)
        {
            if(addedObject != null)
            {
                (addedObject as FrameworkElement).PreviewMouseDown +=  Grid_PreviewMouseDown;
            }
            else if(removedObject != null)
            {
                (addedObject as FrameworkElement).PreviewMouseDown -= Grid_PreviewMouseDown;
            }

            base.OnVisualChildrenChanged(addedObject, removedObject);

            UpdateList();
        }

        #endregion

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

        #region 리스트 업데이트하기 - UpdateList()

        /// <summary>
        /// 리스트 업데이트하기
        /// </summary>
        private void UpdateList()
        {
            if(!IsInitialized)
            {
                return;
            }

            int itemCount = Children.Count;

            ItemAngle = 360.0 / itemCount;

            if(IsItemsHost == true)
            {
                FrameworkElement Parent = TemplatedParent as FrameworkElement;

                ItemWidth  = (Parent.Width * Math.PI) / (itemCount * 1.5);
                ItemHeight = Parent.Height / 2;
            }
            else
            {
                ItemWidth  = (this.ActualWidth * Math.PI) / (itemCount * 1.5);
                ItemHeight = this.ActualHeight / 2;
            }

            int i = 0;
            
            foreach(FrameworkElement child in Children)
            {
                child.Width  = ItemWidth;
                child.Height = ItemHeight;

                child.Rotate(i * ItemAngle);

                i++;
            }

            TranslateTransform.Y = -ItemHeight / 2;

            RotateTransform.CenterY = ItemHeight / 2;
        }

        #endregion
    }
}

 

300x250

 

▶ 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="Grid 클래스 : 환형 패널 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Grid>
        <ListBox Name="listBox"
            Width="400"
            Height="400">
            <ListBox.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
                    Color="Transparent" />
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}"
                    Color="Transparent" />
            </ListBox.Resources>
            <ListBox.Template>
                <ControlTemplate>
                    <local:CircularGrid IsItemsHost="True" />
                </ControlTemplate>
            </ListBox.Template>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="맥주"
                    Source="IMAGE/beer.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="메달"
                    Source="IMAGE/medal.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="연필"
                    Source="IMAGE/pencil.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="방사능"
                    Source="IMAGE/radiation.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="반지"
                    Source="IMAGE/ring.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="맥주"
                    Source="IMAGE/beer.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="메달"
                    Source="IMAGE/medal.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="연필"
                    Source="IMAGE/pencil.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="방사능"
                    Source="IMAGE/radiation.png" />
            </Grid>
            <Grid>
                <Rectangle
                    Width="40"
                    Height="100"
                    Fill="Gray"
                    Opacity="0.5" />
                <Image
                    Width="32"
                    Height="32"
                    Tag="반지"
                    Source="IMAGE/ring.png" />
            </Grid>
        </ListBox>
        <TextBlock
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            FontSize="20"
            FontWeight="Bold"
            Text="{Binding ElementName=listBox, Path=SelectedValue.Children[1].Tag}" />
    </Grid>
</Window>
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요