練手WPF(一)——模擬時鐘與數字時鐘的製做(上)

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();
}

 

看最後效果

相關文章
相關標籤/搜索