1、Visual Studio建立一個WPF項目。git
簡單調整一下MainWindow.xaml文件。主要使用了兩個Canvas控件,分別用於顯示模擬和數字時鐘,命名爲AnalogCanvas、digitCanvas。代碼以下:函數
<Window x:Class="MoonClock.MainWindow" ... Title="Moon Clock" Height="600" Width="1280" WindowStartupLocation="CenterScreen"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Canvas Grid.Column="0" Name="AnalogCanvs" Width="500" Height="500" /> <Canvas Grid.Column="1" Name="digitCanvas" Width="600" Height="300" /> </Grid> </Window>
2、模擬時鐘先來。spa
(1)在MainWindow.xaml.cs先定義幾個字段變量指針
// 共用字段 DispatcherTimer timer = new DispatcherTimer(); // 計時器 DateTime CurrTime = DateTime.Now; // 當前時間 // 模擬時鐘字段定義 double radius = 250; // 圓半徑 double angle = 360; // 角度 Point Opos = new Point(); // 原點位置 Line HourLine, MinuLine, SecdLine; // 時針、分針、秒針
這幾個變量足夠了。code
(2)在構造函數初始化變量blog
public MainWindow() { InitializeComponent(); // 原點位置 Opos = new Point(250, 250); // 初始化計時器 timer.Interval = TimeSpan.FromMilliseconds(100); timer.Tick += Timer_Tick; // 初始化時鐘針 HourLine = new Line(); MinuLine = new Line(); SecdLine = new Line(); }
(3)定義幾個畫表盤的方法事件
都是先定義圖形,而後添加到AngleCanvas中顯示出來ip
/// <summary> /// 畫表盤外圓 /// </summary> private void DrawCircle() { Ellipse ellipse = new Ellipse(); ellipse.Stroke = Brushes.DarkGray; ellipse.StrokeThickness = 4; ellipse.Width = 500; ellipse.Height = 500; ellipse.Fill = Brushes.Gray; Canvas.SetLeft(ellipse, 0); Canvas.SetTop(ellipse, 0); AnalogCanvs.Children.Add(ellipse); Ellipse ellipse1 = new Ellipse(); ellipse1.Stroke = Brushes.Gray; ellipse1.StrokeThickness = 2; ellipse1.Width = 510; ellipse1.Height = 510; Canvas.SetLeft(ellipse1, -5); Canvas.SetTop(ellipse1, -5); AnalogCanvs.Children.Add(ellipse1); }
/// <summary> /// 圓形表心圓 /// </summary> private void DrawOCircle() { Ellipse ellipseO = new Ellipse(); ellipseO.Width = 30; ellipseO.Height = 30; ellipseO.Fill = Brushes.DarkGray; Canvas.SetLeft(ellipseO, Opos.X - 15); Canvas.SetTop(ellipseO, Opos.Y - 15); if (AnalogCanvs.Children.Contains(ellipseO)) AnalogCanvs.Children.Remove(ellipseO); AnalogCanvs.Children.Add(ellipseO); }
/// <summary> /// 畫圓錶盤數字 /// </summary> private void DrawDigit() { double x, y; for (int i=1; i<13; i++) { angle = WrapAngle(i * 360.0 / 12.0) - 90.0; angle = ConvertDegreesToRadians(angle); x = Opos.X + Math.Cos(angle) * (radius - 36) - 8; y = Opos.Y + Math.Sin(angle) * (radius - 36) - 15; TextBlock digit = new TextBlock(); digit.FontSize = 26; digit.Text = i.ToString(); // 數字12位置校訂 if (i == 12) { Canvas.SetLeft(digit, x - 8); } else { Canvas.SetLeft(digit, x); } Canvas.SetTop(digit, y); AnalogCanvs.Children.Add(digit); } }
這裏ConvertDegreesToRadians方法後面再定義,用於將角度值轉換爲弧度。it
繼續畫:io
/// <summary> /// 畫圓表刻度 /// </summary> private void DrawGridLine() { double x1 = 0, y1 = 0; double x2 = 0, y2 = 0; for (int i = 0; i < 60; i++) { double angle1 = WrapAngle(i * 360.0 / 60.0) - 90; angle1 = ConvertDegreesToRadians(angle1); if (i % 5 == 0) { x1 = Math.Cos(angle1) * (radius - 20); y1 = Math.Sin(angle1) * (radius - 20); } else { x1 = Math.Cos(angle1) * (radius - 10); y1 = Math.Sin(angle1) * (radius - 10); } x2 = Math.Cos(angle1) * (radius - 5); y2 = Math.Sin(angle1) * (radius - 5); Line line = new Line(); line.X1 = x1; line.Y1 = y1; line.X2 = x2; line.Y2 = y2; line.Stroke = Brushes.Black; line.StrokeThickness = 3; Canvas.SetLeft(line, Opos.X); Canvas.SetTop(line, Opos.Y); AnalogCanvs.Children.Add(line); } }
以上兩個畫刻度和畫表盤數字方法原理是同樣的,就是先計算角度,再與半徑計算爲位置,以後將刻度或數字圖形畫到AngleCanvas中。
(4)是時候畫時針了。
先來短黑粗的小時針
/// <summary> /// 畫時針 /// </summary> private void DrawHourLine() { int hour = CurrTime.Hour; int minu = CurrTime.Minute; double dminu = minu / 60.0; // 根據分鐘數增長時針偏移 double dhour = hour + dminu; double hour_angle = WrapAngle(dhour * (360.0 / 12.0) - 90.0); hour_angle = ConvertDegreesToRadians(hour_angle); double x = Math.Cos(hour_angle) * (radius - 100); double y = Math.Sin(hour_angle) * (radius - 100); HourLine.X1 = 0; HourLine.Y1 = 0; HourLine.X2 = x; HourLine.Y2 = y; HourLine.Stroke = Brushes.Black; HourLine.StrokeThickness = 16; Canvas.SetLeft(HourLine, Opos.X); Canvas.SetTop(HourLine, Opos.Y); if(AnalogCanvs.Children.Contains(HourLine)) { AnalogCanvs.Children.Remove(HourLine); } AnalogCanvs.Children.Add(HourLine); }
其中註釋句用於根據分鐘數增長小時指針的偏移。若是沒有增長這一偏移,會是每小時跳一次小時指針,現實中的模擬時鐘是不存在這種狀況的。
再來秒鐘指針:
/// <summary> /// 畫秒針 /// </summary> private void DrawSecondLine() { int second = CurrTime.Second; // 秒針正方向點 double se_angle = WrapAngle(second * (360.0 / 60.0) - 90); se_angle = ConvertDegreesToRadians(se_angle); double sec_x = Math.Cos(se_angle) * (radius - 40); double sec_y = Math.Sin(se_angle) * (radius - 40); // 秒針反方向點 se_angle = WrapAngle(second * (360.0 / 60.0) + 90); se_angle = ConvertDegreesToRadians(se_angle); double sec_x_ = Math.Cos(se_angle) * (radius - 180); double sec_y_ = Math.Sin(se_angle) * (radius - 180); SecdLine.X1 = sec_x_; SecdLine.Y1 = sec_y_; SecdLine.X2 = sec_x; SecdLine.Y2 = sec_y; SecdLine.Stroke = Brushes.Red; SecdLine.StrokeThickness = 4; Canvas.SetLeft(SecdLine, Opos.X); Canvas.SetTop(SecdLine, Opos.Y); if (AnalogCanvs.Children.Contains(SecdLine)) { AnalogCanvs.Children.Remove(SecdLine); } AnalogCanvs.Children.Add(SecdLine); }
反方向點用於肯定秒鐘指針通過原點另外一端的位置。通常秒鐘在圓點兩端都會伸出,只是兩端長短不一樣而已。
分鐘就留給有心人練手吧,這裏就不貼了。
(5)接近最後——兩個輔助方法
/// <summary> /// 角度360度進制 /// </summary> /// <param name="angle"></param> /// <returns></returns> private double WrapAngle(double angle) { return angle % 360; } /// <summary> /// 將角度轉爲弧度 /// </summary> /// <param name="degrees"></param> /// <returns></returns> private double ConvertDegreesToRadians(double degrees) { double radians = (Math.PI / 180) * degrees; return radians; }
(6)更新方法:方法中的幾項內容是須要根據時間更新的。
/// <summary> /// 更新小時針、分針、秒針 /// </summary> private void Update() { DrawHourLine(); DrawMinuteLine(); DrawSecondLine(); DrawOCircle(); }
(7)定義計時器事件
/// <summary> /// 計時器事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Timer_Tick(object sender, EventArgs e) { // 更新當前時間 CurrTime = DateTime.Now; // 更新圓盤時針 Update(); }
看最後效果