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

■ 하이브리드 시계를 만드는 방법을 보여준다.

TestProject.zip
다운로드

▶ 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
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요