728x90
반응형
728x170
■ 하이브리드 시계를 만드는 방법을 보여준다.
▶ ClockTicker.cs
using System;
using System.ComponentModel;
using System.Windows.Threading;
namespace TestProject
{
/// <summary>
/// 시계 티커
/// </summary>
public class ClockTicker : INotifyPropertyChanged
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Declaration
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Event
/// <summary>
/// 속성 변경시
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 포맷
/// </summary>
private string format = "F";
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 포맷 - Format
/// <summary>
/// 포맷
/// </summary>
public string Format
{
set
{
this.format = value;
}
get
{
return this.format;
}
}
#endregion
#region 일시 - DateTime
/// <summary>
/// 일시
/// </summary>
public string DateTime
{
get
{
return System.DateTime.Now.ToString(this.format);
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - ClockTicker()
/// <summary>
/// 생성자
/// </summary>
public ClockTicker()
{
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = TimeSpan.FromSeconds(0.10);
dispatcherTimer.Start();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 디스패처 타이머 틱 발생시 처리하기 - dispatcherTimer_Tick(sender, e)
/// <summary>
/// 디스패처 타이머 틱 발생시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DateTime"));
}
}
#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:src="clr-namespace:TestProject"
Width="800"
Height="600"
Title="하이브리드 시계 사용하기"
FontFamily="나눔고딕코딩"
FontSize="16">
<Window.Resources>
<src:ClockTicker x:Key="ClockTickerKey" />
</Window.Resources>
<Viewbox>
<Grid>
<Ellipse>
<Ellipse.Fill>
<SolidColorBrush Color="{x:Static src:MainWindow.BackgroundColor}" />
</Ellipse.Fill>
</Ellipse>
<Grid Name="grid" Margin="12">
<StackPanel
VerticalAlignment="Center"
Orientation="Horizontal"
Opacity="0">
<TextBlock Name="dateTimeTextBlock"
Text="{Binding Source={StaticResource ClockTickerKey}, Path=DateTime}" />
<TextBlock
Text="{Binding ElementName=dateTimeTextBlock, Path=Text}" />
</StackPanel>
<StackPanel
HorizontalAlignment="Center"
Orientation="Horizontal"
Opacity="0">
<TextBlock Text="{Binding ElementName=dateTimeTextBlock, Path=Text}" />
<TextBlock Text="{Binding ElementName=dateTimeTextBlock, Path=Text}" />
<StackPanel.LayoutTransform>
<RotateTransform Angle="90" />
</StackPanel.LayoutTransform>
</StackPanel>
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal"
RenderTransformOrigin="0.5 0.5">
<TextBlock
Opacity="0"
Text="{Binding ElementName=dateTimeTextBlock, Path=Text}" />
<TextBlock Name="textBlock1"
Opacity="0.5"
Text="{Binding ElementName=dateTimeTextBlock, Path=Text}" />
<StackPanel.RenderTransform>
<RotateTransform x:Name="rotateTransform1"/>
</StackPanel.RenderTransform>
</StackPanel>
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal"
RenderTransformOrigin="0.5 0.5">
<TextBlock Name="textBlock2"
Opacity="0.5"
Text="{Binding ElementName=dateTimeTextBlock, Path=Text}" />
<TextBlock
Opacity="0"
Text="{Binding ElementName=dateTimeTextBlock, Path=Text}" />
<StackPanel.RenderTransform>
<RotateTransform x:Name="rotateTransform2"/>
</StackPanel.RenderTransform>
</StackPanel>
</Grid>
<Ellipse Name="maskEllipse"
RenderTransformOrigin="0.5 0.5" >
<Ellipse.RenderTransform>
<RotateTransform x:Name="rotateTransform3"/>
</Ellipse.RenderTransform>
</Ellipse>
</Grid>
</Viewbox>
<Window.Triggers>
<EventTrigger RoutedEvent="Page.Loaded">
<BeginStoryboard>
<Storyboard x:Name="storyboard">
<DoubleAnimation
Storyboard.TargetName="rotateTransform1"
Storyboard.TargetProperty="Angle"
RepeatBehavior="Forever"
Duration="0:1:0"
From="-90"
To="270" />
<DoubleAnimation
Storyboard.TargetName="rotateTransform2"
Storyboard.TargetProperty="Angle"
RepeatBehavior="Forever"
Duration="0:1:0"
From="-270"
To="90" />
<DoubleAnimation
Storyboard.TargetName="rotateTransform3"
Storyboard.TargetProperty="Angle"
Duration="0:1:0"
From="-90"
To="270"
RepeatBehavior="Forever" />
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="textBlock1"
Storyboard.TargetProperty="Opacity"
RepeatBehavior="Forever"
Duration="0:1:0">
<LinearDoubleKeyFrame KeyTime="0:0:0.5" Value="1" />
<DiscreteDoubleKeyFrame KeyTime="0:0:29.5" Value="1" />
<LinearDoubleKeyFrame KeyTime="0:0:30.5" Value="0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:59.5" Value="0" />
<LinearDoubleKeyFrame KeyTime="0:1:0" Value="0.5" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="textBlock2"
Storyboard.TargetProperty="Opacity"
RepeatBehavior="Forever"
Duration="0:1:0">
<LinearDoubleKeyFrame KeyTime="0:0:0.5" Value="0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:29.5" Value="0" />
<LinearDoubleKeyFrame KeyTime="0:0:30.5" Value="1" />
<DiscreteDoubleKeyFrame KeyTime="0:0:59.5" Value="1" />
<LinearDoubleKeyFrame KeyTime="0:1:0" Value="0.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
</Window>
▶ MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace TestProject
{
/// <summary>
/// 메인 윈도우
/// </summary>
public partial class MainWindow : Window
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 배경색
/// </summary>
public static readonly Color BackgroundColor = Colors.Aqua;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 이동 변환 배열
/// </summary>
private TranslateTransform[] translateTransformArray = new TranslateTransform[60];
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainWindow
/// <summary>
/// 생성자
/// </summary>
public MainWindow()
{
InitializeComponent();
this.storyboard.BeginTime = -DateTime.Now.TimeOfDay;
ContextMenu contextMenu = new ContextMenu();
contextMenu.Opened += contextMenu_Opened;
ContextMenu = contextMenu;
Loaded += Page_Loaded;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
//////////////////////////////////////////////////////////////////////////////// Event
#region 페이지 로드시 처리하기 - Page_Loaded(sender, e)
/// <summary>
/// 페이지 로드시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void Page_Loaded(object sender, EventArgs e)
{
for(int i = 0; i < 60; i++)
{
Ellipse ellipse = new Ellipse();
ellipse.HorizontalAlignment = HorizontalAlignment.Center;
ellipse.VerticalAlignment = VerticalAlignment.Center;
ellipse.Width = i % 5 == 0 ? 6 : 2;
ellipse.Height = i % 5 == 0 ? 6 : 2;
ellipse.Fill = Brushes.RoyalBlue;
TransformGroup transformGroup = new TransformGroup();
this.translateTransformArray[i] = new TranslateTransform(this.dateTimeTextBlock.ActualWidth, 0);
transformGroup.Children.Add(this.translateTransformArray[i]);
transformGroup.Children.Add(new TranslateTransform(grid.Margin.Left / 2, 0));
transformGroup.Children.Add(new TranslateTransform(-ellipse.Width / 2, -ellipse.Height / 2));
transformGroup.Children.Add(new RotateTransform(i * 6));
transformGroup.Children.Add(new TranslateTransform(ellipse.Width / 2, ellipse.Height / 2));
ellipse.RenderTransform = transformGroup;
this.grid.Children.Add(ellipse);
}
SetMask();
this.dateTimeTextBlock.SizeChanged += dateTimeTextBlock_SizeChanged;
}
#endregion
#region 컨텍스트 메뉴 오픈시 처리하기 - contextMenu_Opened(sender, e)
/// <summary>
/// 컨텍스트 메뉴 오픈시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void contextMenu_Opened(object sender, RoutedEventArgs e)
{
ContextMenu contextMenu = sender as ContextMenu;
contextMenu.Items.Clear();
string[] formatArray =
{
"d", "D", "f", "F", "g", "G", "M",
"R", "s", "t", "T", "u", "U", "Y"
};
foreach(string format in formatArray)
{
MenuItem menuItem = new MenuItem();
menuItem.Header = DateTime.Now.ToString(format);
menuItem.Tag = format;
menuItem.IsChecked = format == (Resources["ClockTickerKey"] as ClockTicker).Format;
menuItem.Click += menuItem_Click;
contextMenu.Items.Add(menuItem);
}
}
#endregion
#region 메뉴 항목 클릭시 처리하기 - menuItem_Click(sender, e)
/// <summary>
/// 메뉴 항목 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void menuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
(Resources["ClockTickerKey"] as ClockTicker).Format = menuItem.Tag as string;
}
#endregion
#region 일시 텍스트 블럭 크기 변경시 처리하기 - dateTimeTextBlock_SizeChanged(sender, e)
/// <summary>
/// 일시 텍스트 블럭 크기 변경시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void dateTimeTextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
{
if(e.WidthChanged)
{
for(int i = 0; i < 60; i++)
{
this.translateTransformArray[i].X = this.dateTimeTextBlock.ActualWidth;
}
SetMask();
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region 마스크 설정하기 - SetMask()
/// <summary>
/// 마스크 설정하기
/// </summary>
private void SetMask()
{
DrawingGroup drawingGroup = new DrawingGroup();
Point centerPoint = new Point(this.dateTimeTextBlock.ActualWidth + this.grid.Margin.Left, this.dateTimeTextBlock.ActualWidth + this.grid.Margin.Left);
for(int i = 0; i < 256; i++)
{
Point innerPoint1 = new Point
(
centerPoint.X + this.dateTimeTextBlock.ActualWidth * Math.Cos(i * 2 * Math.PI / 256),
centerPoint.Y + this.dateTimeTextBlock.ActualWidth * Math.Sin(i * 2 * Math.PI / 256)
);
Point innerPoint2 = new Point
(
centerPoint.X + this.dateTimeTextBlock.ActualWidth * Math.Cos((i + 2) * 2 * Math.PI / 256),
centerPoint.Y + this.dateTimeTextBlock.ActualWidth * Math.Sin((i + 2) * 2 * Math.PI / 256)
);
Point outerPoint1 = new Point
(
centerPoint.X + (this.dateTimeTextBlock.ActualWidth + this.grid.Margin.Left) * Math.Cos(i * 2 * Math.PI / 256),
centerPoint.Y + (this.dateTimeTextBlock.ActualWidth + this.grid.Margin.Left) * Math.Sin(i * 2 * Math.PI / 256)
);
Point outerPoint2 = new Point
(
centerPoint.X + (this.dateTimeTextBlock.ActualWidth + this.grid.Margin.Left) * Math.Cos((i + 2) * 2 * Math.PI / 256),
centerPoint.Y + (this.dateTimeTextBlock.ActualWidth + this.grid.Margin.Left) * Math.Sin((i + 2) * 2 * Math.PI / 256)
);
PathSegmentCollection pathSegmentCollection = new PathSegmentCollection();
pathSegmentCollection.Add(new LineSegment(innerPoint2, false));
pathSegmentCollection.Add(new LineSegment(outerPoint2, false));
pathSegmentCollection.Add(new LineSegment(outerPoint1, false));
pathSegmentCollection.Add(new LineSegment(innerPoint1, false));
PathFigure pathFigure = new PathFigure(innerPoint1, pathSegmentCollection, true);
PathFigureCollection pathFigureCollection = new PathFigureCollection();
pathFigureCollection.Add(pathFigure);
PathGeometry pathGeometry = new PathGeometry(pathFigureCollection);
byte opacity = (byte)Math.Min(255, 512 - 2 * i);
SolidColorBrush solidColorBrush = new SolidColorBrush(Color.FromArgb(opacity, BackgroundColor.R, BackgroundColor.G, BackgroundColor.B));
GeometryDrawing pGeometryDrawing = new GeometryDrawing
(
solidColorBrush,
new Pen(solidColorBrush, 2),
pathGeometry
);
drawingGroup.Children.Add(pGeometryDrawing);
}
DrawingBrush drawingBrush = new DrawingBrush(drawingGroup);
this.maskEllipse.Fill = drawingBrush;
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] FormattedText 클래스 : BuildGeometry 메소드를 사용해 텍스트 효과 만들기 (0) | 2018.02.18 |
---|---|
[C#/WPF] 스플라인 키 프레임 애니메이션 사용하기 (0) | 2018.02.18 |
[C#/WPF] Shape 클래스 : 펜 사용하기 (0) | 2018.02.18 |
[C#/WPF] 파티클 애니메이션 사용하기 (0) | 2018.02.18 |
[C#/WPF] 마우스 애니메이션 사용하기 (0) | 2018.02.18 |
[C#/WPF] 마우스 드래그를 사용해 원 그리기 (0) | 2018.02.18 |
[C#/WPF] 도트 폰트 생성하기 (0) | 2018.02.18 |
[C#/WPF] 글자 애니메이션 사용하기 (0) | 2018.02.18 |
[C#/WPF] MarkupExtension 클래스 : 마크업 확장 사용하기 (0) | 2018.02.18 |
[C#/WPF] MediaElement 클래스 : 동영상 재생하기 (0) | 2017.06.14 |
댓글을 달아 주세요