zoukankan      html  css  js  c++  java
  • 练手WPF(一)——模拟时钟与数字时钟的制作(上)

    一、Visual Studio创建一个WPF项目。

    简单调整一下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>

    二、模拟时钟先来。

    (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;           // 时针、分针、秒针

    这几个变量足够了。

    (2)在构造函数初始化变量

    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中显示出来

    /// <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方法后面再定义,用于将角度值转换为弧度。

    继续画:

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

    看最后效果

  • 相关阅读:
    sed命令:删除匹配行和替换
    使用git rebase合并多次commit
    解决flask中文乱码的问题
    PyCharm 2017: Remote debugging using remote interpreter doesn't work
    ansible小结(八)ansible-playbook简单使用
    ansible命令参数介绍
    eclipse 代码自动提示
    oracle start with connect by prior 递归查询
    Android九宫图(draw9patch)
    Android 通过按钮弹出系统菜单(通过Button显示菜单)转
  • 原文地址:https://www.cnblogs.com/moonblogcore/p/10920574.html
Copyright © 2011-2022 走看看