2018/12/13

[C#] WPF Analog Clock

這篇用WPF 來寫一個可以顯示時鐘的APP。
如果只是要顯示數字形式的Digital Clock很簡單,但如果是想要做指針形式的Analog Clock 的話,就要加一些數學去算圖案的旋轉角度。
程式的架構概念,設定一個計時器,每秒會去抓系統目前的時間,在把抓到的值換算成旋轉的角度。
second、minute、hour 算出來的角度,分別對應到秒針、分針跟時針的圖案。

先準備好三張指針的圖片,分別是秒針、分針跟時針。
用DispatcherTimer 設定計時器的寫法之前有寫過(參考這篇)
在計時器中利用以下函式,分別去抓目前系統的時間(秒、分、時)
sec = DateTime.Now.Second;
min = DateTime.Now.Minute;
hr = DateTime.Now.Hour;

注意,這邊因為圖案設計的關係,在一開始必須先把旋轉的中心點設定在圖片的中間底部,
用RenderTransformOrigin 來設定,這樣轉才會正確。
secondPointer.RenderTransformOrigin = new Point(0.5, 1);
minutePointer.RenderTransformOrigin = new Point(0.5, 1);
hourPointer.RenderTransformOrigin = new Point(0.5, 1);

接著把得到的值,分別算成對應旋轉的角度,然後去旋轉對應的圖片物件
secondPointer.RenderTransform = new RotateTransform((sec / 60) * 360);
minutePointer.RenderTransform = new RotateTransform((min * 360 / 60) + ((sec / 60) * 6));
hourPointer.RenderTransform = new RotateTransform((hr * 360 / 12) + (min / 2));

附上完整的程式碼參考
MainWindow.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace ClockApp
{
    public partial class MainWindow : Window
    {
        double sec, min, hr;
        DispatcherTimer clock = new DispatcherTimer();

        public MainWindow()
        {
            InitializeComponent();

            // set RenderTransformOrigin
            secondPointer.RenderTransformOrigin = new Point(0.5, 1);
            minutePointer.RenderTransformOrigin = new Point(0.5, 1);
            hourPointer.RenderTransformOrigin = new Point(0.5, 1);

            // set timer
            clock.Interval = TimeSpan.FromSeconds(1);
            clock.Tick += clock_Tick;
            clock.Start();

            setCurrentTime();
        }

        void clock_Tick(object sender, EventArgs e)
        {
            setCurrentTime();
        }

        private void setCurrentTime()
        {
            // get current time
            sec = DateTime.Now.Second;
            min = DateTime.Now.Minute;
            hr = DateTime.Now.Hour;

            // rotate pointer image
            secondPointer.RenderTransform = new RotateTransform((sec / 60) * 360);
            minutePointer.RenderTransform = new RotateTransform((min * 360 / 60) + ((sec / 60) * 6));
            hourPointer.RenderTransform = new RotateTransform((hr * 360 / 12) + (min / 2));
        }
    }
}


MainWindow.xaml:
<Window x:Name="mMainWindow" x:Class="ClockApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ClockApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="768" Width="1024" Left="0" Top="0" WindowStyle="None" Topmost="True" AllowsTransparency="True">
    <Grid>
        <Image x:Name="clockface" Source="images/bg_1280x720-01.png" Height="768" Width="1024"/>
        <Image x:Name="hourPointer" Source="images/pointer_hr.png" Height="200" Width="41" Stretch="Fill" Margin="0,-200,0,0"/>
        <Image x:Name="minutePointer" Source="images/pointer_min.png" Height="200" Width="16" Stretch="Fill" Margin="0,-200,0,0"/>
        <Image x:Name="secondPointer" Source="images/pointer_sec.png" Height="200" Width="7" Stretch="Fill" Margin="0,-200,0,0"/>
    </Grid>
</Window>


END

沒有留言:

張貼留言