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

728x90
반응형

TestProject.zip
0.06MB

▶ FishEyeControl.cs

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

namespace TestProject
{
    /// <summary>
    /// 물고기 눈 컨트롤
    /// </summary>
    public class FishEyeControl : StackPanel
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 애니메이션 밀리초 속성 - AnimationMillisecondsProperty

        /// <summary>
        /// 애니메이션 밀리초 속성
        /// </summary>
        public static readonly DependencyProperty AnimationMillisecondsProperty = DependencyProperty.Register
        (
            "AnimationMilliseconds",
            typeof(int),
            typeof(FishEyeControl),
            new UIPropertyMetadata(65)
        );

        #endregion

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

        #region Field

        /// <summary>
        /// 엘리먼트 너비
        /// </summary>
        private double elementWidth;

        #endregion

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

        #region 애니메이션 밀리초 - AnimationMilliseconds

        /// <summary>
        /// 애니메이션 밀리초
        /// </summary>
        public int AnimationMilliseconds
        {
            get
            {
                return (int)GetValue(AnimationMillisecondsProperty);
            }
            set
            {
                SetValue(AnimationMillisecondsProperty, value);
            }
        }

        #endregion

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

        #region 생성자 - FishEyeControl()

        /// <summary>
        /// 생성자
        /// </summary>
        public FishEyeControl()
        {
            HorizontalAlignment = HorizontalAlignment.Center;
            Background          = Brushes.Transparent;
            Orientation         = Orientation.Horizontal;

            Loaded     += StackPanel_Loaded;
            MouseEnter += StackPanel_MouseEnter;
            MouseMove  += StackPanel_MouseMove;
            MouseLeave += StackPanel_MouseLeave;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

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

        /// <summary>
        /// 측정하기 (오버라이드)
        /// </summary>
        /// <param name="availableSize">이용 가능한 크기</param>
        /// <returns>측정 크기</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            UIElementCollection childCollection = InternalChildren;

            Size size  = new Size();
            int  i     = 0;
            int  count = childCollection.Count;

            availableSize.Width = double.PositiveInfinity;

            while(i < count)
            {
                UIElement element = childCollection[i];

                if(element != null)
                {
                    element.Measure((Size)availableSize);

                    Size desiredSize = element.DesiredSize;

                    size.Width += Math.Round(desiredSize.Width, 2);

                    size.Height = Math.Max(size.Height, desiredSize.Height);
                }

                i++;
            }

            return size;
        }

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

        /// <summary>
        /// 배열하기 (오버라이드)
        /// </summary>
        /// <param name="arrangeSize">배열 크기</param>
        /// <returns>배열 크기</returns>
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            if(Children == null || Children.Count == 0)
            {
                return arrangeSize;
            }

            UIElementCollection childCollection = Children;

            Rect finalRectangle = new Rect(arrangeSize);

            double width = 0d;
            int    i     = 0;
            int    count = childCollection.Count;

            while(i < count)
            {
                UIElement element = childCollection[i];

                if(element != null)
                {
                    finalRectangle.X += width;

                    width = element.DesiredSize.Width;

                    finalRectangle.Width  = width;
                    finalRectangle.Height = Math.Max(arrangeSize.Height, element.DesiredSize.Height);

                    element.SetValue(WidthProperty, element.DesiredSize.Width);

                    if(element.RenderTransform as TransformGroup == null)
                    {
                        TransformGroup transformGroup = new TransformGroup();

                        element.RenderTransform = transformGroup;

                        transformGroup.Children.Add(new ScaleTransform()    );
                        transformGroup.Children.Add(new TranslateTransform());

                        element.RenderTransformOrigin = new Point(0.5, 1);
                    }

                    element.Arrange(finalRectangle);
                }

                i++;
            }

            AnimateAll();

            return arrangeSize;
        }

        #endregion

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

        #region 스택 패널 로드시 처리하기 - StackPanel_Loaded(sender, e)

        /// <summary>
        /// 스택 패널 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void StackPanel_Loaded(object sender, RoutedEventArgs e)
        {
            if(Children == null || Children.Count == 0)
            {
                return;
            }

            foreach(UIElement child in Children)
            {
                this.elementWidth += child.DesiredSize.Width;
            }

            this.elementWidth /= Children.Count;

            InvalidateArrange();
        }

        #endregion
        #region 스택 패널 마우스 진입시 처리하기 - StackPanel_MouseEnter(sender, e)

        /// <summary>
        /// 스택 패널 마우스 진입시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void StackPanel_MouseEnter(object sender, MouseEventArgs e)
        {
            InvalidateArrange();
        }

        #endregion
        #region 스택 패널 마우스 이동시 처리하기 - StackPanel_MouseMove(sender, e)

        /// <summary>
        /// 스택 패널 마우스 이동시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void StackPanel_MouseMove(object sender, MouseEventArgs e)
        {
            InvalidateArrange();
        }

        #endregion
        #region 스택 패널 마우스 이탈시 처리하기 - StackPanel_MouseLeave(sender, e)

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

        #endregion

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

        #region 애니메이션 구하기 - GetAnimation(to, duration)

        /// <summary>
        /// 애니메이션 구하기
        /// </summary>
        /// <param name="to">목표</param>
        /// <param name="duration">지속 시간</param>
        /// <returns>실수 애니메이션</returns>
        private DoubleAnimation GetAnimation(double to, double duration)
        {
            DoubleAnimation animation = new DoubleAnimation(to, TimeSpan.FromMilliseconds(duration))
            {
                AccelerationRatio = 0.2,
                DecelerationRatio = 0.1
            };

            return animation;
        }

        #endregion
        #region 애니메이션 처리하기 - AnimateTo(child, scale, width, duration)

        /// <summary>
        /// 애니메이션 처리하기
        /// </summary>
        /// <param name="child">자식</param>
        /// <param name="scale">스케일</param>
        /// <param name="width">너비</param>
        /// <param name="duration">지속 시간</param>
        private void AnimateTo(UIElement child, double scale, double width, double duration)
        {
            TransformGroup transformGroup = child.RenderTransform as TransformGroup;

            ScaleTransform scaleTransform = transformGroup.Children[0] as ScaleTransform;

            scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, GetAnimation(scale, duration));
            scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, GetAnimation(scale, duration));

            child.BeginAnimation(WidthProperty, GetAnimation(width, duration));
        }

        #endregion
        #region 모두 애니메이션 처리하기 - AnimateAll()

        /// <summary>
        /// 모두 애니메이션 처리하기
        /// </summary>
        private void AnimateAll()
        {
            if(Children == null || Children.Count == 0)
            {
                return;
            }

            int? selectedElement = null;

            if(IsMouseOver)
            {
                double width = 0d;
                double x     = Mouse.GetPosition(this).X;
                double y     = Mouse.GetPosition(this).Y;

                for(int i = 0; i < Children.Count; i++)
                {
                    if(Children[i].IsMouseOver && y < Children[i].DesiredSize.Height)
                    {
                        selectedElement = i;

                        break;
                    }

                    width += Children[i].DesiredSize.Width;

                    if(x <= width && y < Children[i].DesiredSize.Height)
                    {
                        selectedElement = i;
                        break;
                    }
                }
            }

            for(int i = 0; i < Children.Count; i++)
            {
                if(i == selectedElement - 2)
                {
                    AnimateTo(Children[i], 1.25, (this.elementWidth * 1.5), AnimationMilliseconds);
                }
                else if(i == selectedElement - 1)
                {
                    AnimateTo(Children[i], 1.5, (this.elementWidth * 2.0), AnimationMilliseconds);
                }
                else if(i == selectedElement)
                {
                    AnimateTo(Children[i], 2, (this.elementWidth * 2.5), AnimationMilliseconds);
                }
                else if(i == selectedElement + 1)
                {
                    AnimateTo(Children[i], 1.5, (this.elementWidth * 2.0), AnimationMilliseconds);
                }
                else if(i == selectedElement + 2)
                {
                    AnimateTo(Children[i], 1.25, (this.elementWidth * 1.5), AnimationMilliseconds);
                }
                else
                {
                    AnimateTo(Children[i], 1, this.elementWidth, AnimationMilliseconds);
                }
            }
        }

        #endregion
    }
}

 

728x90

 

▶ ReflectionControl.cs

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

namespace TestProject
{
    /// <summary>
    /// 반사 컨트롤
    /// </summary>
    public class ReflectionControl : Decorator
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 반사 비주얼 브러시
        /// </summary>
        private readonly VisualBrush reflectionVisualBrush;

        /// <summary>
        /// 불투명도 마스크 선형 그라디언트 브러시
        /// </summary>
        private readonly LinearGradientBrush opacityMaskLinearGradientBrush;

        #endregion

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

        #region 생성자 - ReflectionControl()

        /// <summary>
        /// 생성자
        /// </summary>
        public ReflectionControl()
        {
            HorizontalAlignment = HorizontalAlignment.Center;
            VerticalAlignment   = VerticalAlignment.Bottom;

            this.opacityMaskLinearGradientBrush = new LinearGradientBrush
            {
                StartPoint = new Point(0, 0  ),
                EndPoint   = new Point(0, 0.8)
            };

            this.opacityMaskLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Black      , 0  ));
            this.opacityMaskLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Black      , 0.5));
            this.opacityMaskLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Transparent, 0.8));
            this.opacityMaskLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Transparent, 1  ));

            this.reflectionVisualBrush = new VisualBrush
            {
                Transform = new ScaleTransform(1, -1)
            };
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

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

        /// <summary>
        /// 측정하기 (오버라이드)
        /// </summary>
        /// <param name="availableSize">이용 가능한 크기</param>
        /// <returns>측정 크기</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            if(Child == null)
            {
                return new Size(0, 0);
            }

            Child.Measure(availableSize);

            return new Size(Child.DesiredSize.Width, Child.DesiredSize.Height * 2.6);
        }

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

        /// <summary>
        /// 배열하기 (오버라이드)
        /// </summary>
        /// <param name="finalSize">최종 크기</param>
        /// <returns>배열 크기</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            if(Child == null)
            {
                return new Size(0, 0);
            }

            Child.Arrange(new Rect(0, -(finalSize.Height * 2), finalSize.Width, finalSize.Height * 2.6));

            return finalSize;
        }

        #endregion
        #region 렌더링시 처리하기 - OnRender(drawingContext)

        /// <summary>
        /// 렌더링시 처리하기
        /// </summary>
        /// <param name="drawingContext">그리기 컨텍스트</param>
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            drawingContext.PushOpacityMask(this.opacityMaskLinearGradientBrush);

            drawingContext.PushOpacity(0.3);

            this.reflectionVisualBrush.Visual = Child;

            ((ScaleTransform)this.reflectionVisualBrush.Transform).CenterY = 4 * ActualHeight / 5;

            drawingContext.DrawRectangle
            (
                this.reflectionVisualBrush,
                null,
                new Rect(0, ActualHeight / 2, ActualWidth, ActualHeight / 2)
            );

            drawingContext.Pop();
            drawingContext.Pop();
        }

        #endregion
    }
}

 

300x250

 

▶ ImageConstant.cs

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

        #region Field

        /// <summary>
        /// 브라우저
        /// </summary>
        public const string BROWSER = @"pack://application:,,,/TestProject;component/IMAGE/browser.png";

        /// <summary>
        /// 달력
        /// </summary>
        public const string CALENDAR = @"pack://application:,,,/TestProject;component/IMAGE/calendar.png";

        /// <summary>
        /// 시계
        /// </summary>
        public const string CLOCK = @"pack://application:,,,/TestProject;component/IMAGE/clock.png";

        /// <summary>
        /// 연락처
        /// </summary>
        public const string CONTACTS = @"pack://application:,,,/TestProject;component/IMAGE/contacts.png";

        /// <summary>
        /// 지도
        /// </summary>
        public const string MAPS = @"pack://application:,,,/TestProject;component/IMAGE/maps.png";

        /// <summary>
        /// 미디어
        /// </summary>
        public const string MEDIA = @"pack://application:,,,/TestProject;component/IMAGE/media.png";

        /// <summary>
        /// 메시지
        /// </summary>
        public const string MESSAGES = @"pack://application:,,,/TestProject;component/IMAGE/messages.png";

        /// <summary>
        /// 프로필
        /// </summary>
        public const string PROFILES = @"pack://application:,,,/TestProject;component/IMAGE/profiles.png";

        /// <summary>
        /// 탐색
        /// </summary>
        public const string SEARCH = @"pack://application:,,,/TestProject;component/IMAGE/search.png";

        #endregion
    }
}

 

반응형

 

▶ Item.cs

using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 항목
    /// </summary>
    public class Item
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 항목명 - ItemName

        /// <summary>
        /// 항목명
        /// </summary>
        public string ItemName { get; set; }

        #endregion
        #region 항목 이미지 - ItemImage

        /// <summary>
        /// 항목 이미지
        /// </summary>
        public ImageSource ItemImage { get; set; }

        #endregion
    }
}

 

▶ DataHelper.cs

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

namespace TestProject
{
    /// <summary>
    /// 데이터 헬퍼
    /// </summary>
    public class DataHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 항목 리스트 구하기 - GetItemList()

        /// <summary>
        /// 항목 리스트 구하기
        /// </summary>
        /// <returns>항목 리스트</returns>
        public static List<Item> GetItemList()
        {
            List<Item> list = new List<Item>
            {
                new Item
                {
                    ItemName  = "메시지",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.MESSAGES)
                },
                new Item
                {
                    ItemName  = "연락처",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.CONTACTS)
                },
                new Item
                {
                    ItemName  = "달력",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.CALENDAR)
                },
                new Item
                {
                    ItemName  = "브라우저",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.BROWSER)
                },
                new Item
                {
                    ItemName  = "미디어",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.MEDIA)
                },
                new Item
                {
                    ItemName  = "지도",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.MAPS)
                },
                new Item
                {
                    ItemName  = "시계",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.CLOCK)
                },
                new Item
                {
                    ItemName  = "탐색",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.SEARCH)
                },
                new Item
                {
                    ItemName  = "프로필",
                    ItemImage = (ImageSource)new ImageSourceConverter().ConvertFrom(ImageConstant.PROFILES)
                }
            };

            return list;
        }

        #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="대시보드 애니메이션 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Window.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <ContentPresenter
                            ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
                            HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
                            VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
                            Margin="{TemplateBinding Control.Padding}"
                            SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
                            RecognizesAccessKey="True"
                            Content="{TemplateBinding ContentControl.Content}" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid Background="Black">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"   />
            <RowDefinition Height="150" />
            <RowDefinition Height="200" />
            <RowDefinition Height="*"   />
        </Grid.RowDefinitions>
        <local:ReflectionControl Grid.Row="1">
            <local:FishEyeControl VerticalAlignment="Bottom">
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.MESSAGES}}" />
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.CONTACTS}}" />
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.CALENDAR}}" />
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.BROWSER }}" />
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.MEDIA   }}" />
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.MAPS    }}" />
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.CLOCK   }}" />
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.SEARCH  }}" />
                <Image Width="32" Height="32" Source="{Binding Source={x:Static local:ImageConstant.PROFILES}}" />
            </local:FishEyeControl>
        </local:ReflectionControl>
        <local:ReflectionControl Grid.Row="2">
            <ItemsControl
                HorizontalAlignment="Center"
                VerticalAlignment="Bottom"
                ItemsSource="{Binding Path=ItemList}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <local:FishEyeControl />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Vertical">
                            <TextBlock Name="itemNameTextBlock"
                                Foreground="#eff7ff"
                                TextAlignment="Center"
                                FontSize="7px"
                                Text="{Binding Path=ItemName}"
                                Visibility="Hidden" />
                            <Button
                                Width="32"
                                Height="32">
                                <Image Source="{Binding Path=ItemImage}" />
                            </Button>
                        </StackPanel>
                        <DataTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter
                                    TargetName="itemNameTextBlock"
                                    Property="Visibility"
                                    Value="Visible" />
                            </Trigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </local:ReflectionControl>
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

using System.Collections.Generic;

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

        #region Field

        /// <summary>
        /// 항목 리스트
        /// </summary>
        private readonly List<Item> itemList;

        #endregion

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

        #region 항목 리스트 - ItemList

        /// <summary>
        /// 항목 리스트
        /// </summary>
        public List<Item> ItemList
        {
            get
            {
                return this.itemList;
            }
        }

        #endregion

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

        #region 생성자 - MainWindow()

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

            DataContext = this;

            this.itemList = DataHelper.GetItemList();
        }

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

댓글을 달아 주세요