728x90
반응형
728x170
▶ 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
반응형
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF/.NETCORE] IValueConverter 인터페이스 : 비트맵→비트맵 소스 변환자 사용하기 (0) | 2021.09.23 |
---|---|
[C#/WPF] ICommand 인터페이스 : 릴레이 명령 사용하기 (0) | 2021.09.23 |
[C#/WPF] ICommand 인터페이스 : 매개 변수를 갖는 대리자 명령 사용하기 (0) | 2021.09.23 |
[C#/WPF] ICommand 인터페이스 : 대리자 명령 사용하기 (0) | 2021.09.23 |
[C#/WPF] ComboBox 클래스 : 우선 순위 필터 콤보 박스 사용하기 (0) | 2021.09.17 |
[C#/WPF] 확장 메뉴 사용하기 (0) | 2021.09.15 |
[C#/WPF] ToggleButton 클래스 : 드롭 다운 버튼/분리 버튼 사용하기 (0) | 2021.09.14 |
[C#/WPF] 열거형 정렬하기 (0) | 2021.09.13 |
[C#/WPF] UserControl 엘리먼트 : 로딩 패널 사용하기 (0) | 2021.09.13 |
[C#/WPF] Adorner 클래스 : 슬라이딩 어도너 사용하기 (0) | 2021.09.13 |
댓글을 달아 주세요