zoukankan      html  css  js  c++  java
  • WPF自定义控件(1)——仪表盘设计[1]

    0、小叙闲言

    又接手一个新的项目了,再来一次上位机开发。网上有很多控件库,做仪表盘(gauge)的也不少,功能也很强大,但是个人觉得库很臃肿,自己就计划动手来写一个控件库,一是为学习,二是为了项目。下面是我花了一下午的时间做出来的,先看效果: 


    这个表盘当前还比较丑,后面会一步一步地完善它的,包括各种美化,相信自己能做到的,加油!!这也是我个人第一次写博客,我会坚持下去,同时也会尽力表述清楚每一个技术细节。源码地址:https://github.com/wj-data/MyGauge

    1、表盘总体设计

     一个表盘,就简单来看,应该由四个部分组成,即:表盘外轮廓、刻度(包括小刻度和大刻度)、刻度值、指针。在制作的过程中,略微用了一些数学知识,只要用心思考,都很容易的。设计外观的过程中,用到了对应如下知识点。当然也包括一些C#和WPF的基础知识,如果有不清楚的地方,可以看看刘铁猛老师的《深入浅出WPF》

    表盘外轮廓 刻度 刻度值 指针
    Path路径绘图 直线 TextBlock控件 Path路径绘图

    2、表盘外轮廓

     初步设计,外轮廓由三段组成:yellow、green、red,借助WPF强大的绘图功能,做了一个渐变色,稍微美化了一下,如下图。(此圆的半径为:200px)

    明显可以看出来,这个圆由三段弧组成的,如果观察仔细的话,可以隐约看到2根小白线,就是三段弧的分界处。

    1.黄色弧绘制

    代码如下:

     1 <Path StrokeThickness="30" Width="420" Height="400" StrokeStartLineCap="Round">
     2     <Path.Data>
     3         <PathGeometry Figures="M 0,200 A 200,200 0 0 1 58.57864,58.57864"/>
     4     </Path.Data>
     5     <Path.Stroke>
     6         <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
     7             <LinearGradientBrush.GradientStops>
     8                 <GradientStop Offset="0" Color="Green"/>
     9                 <GradientStop Offset="1.0" Color="Yellow"/>
    10             </LinearGradientBrush.GradientStops>
    11         </LinearGradientBrush>
    12     </Path.Stroke>
    13 </Path>

    其中最为关键的代码是第3行。对其数据点的解释如下表。有不明白的地方,先记下来,后面也会用到,会慢慢理解的。

    M 0,200 A 200,200 0 0 1 58.57864,58.57864

    M是Path绘图的起点标记

    弧的起点坐标为(0,200)

    A(arc)是弧的标记

    (200,200)表示x轴半径:200;y轴半径:200

    圆弧旋转角度[0](有起点和终点,个人感觉这个值并没有什么用)

    优势弧的标记[0](否,弧角度小于180)

    正负角度标记[1](顺时针画圆)

    表示终点,用数学公式计算出来的

    (58.57864,58.57864)的计算方式如下:

    黄色弧占1/4,故其角度为180*1/4=45度,黄色点的坐标计算如下图。

    2.绿色和红色弧绘制

     有了上面黄色弧绘制作为基础,绿色和红色都是同样的道理,下面直接给出绘制三段弧的代码

     1 <Path Stroke="Yellow" StrokeThickness="30" Width="420" Height="400" StrokeStartLineCap="Round">
     2     <Path.Data>
     3         <PathGeometry Figures="M 0,200 A 200,200 0 0 1 58.57864,58.57864"/>
     4     </Path.Data>
     5 </Path>
     6 <Path Stroke="Green" StrokeThickness="30" Width="420" Height="400">
     7     <Path.Data>
     8         <PathGeometry Figures="M 58.57864,58.57864 A 200,200 0 0 1 341.42136,58.57864" />
     9     </Path.Data>
    10 </Path>
    11 <Path Stroke="Red" StrokeThickness="30" Width="420" Height="400" StrokeEndLineCap="Round">
    12     <Path.Data>
    13         <PathGeometry Figures="M 341.42136,58.57864 A 200,200 0 0 1 400,200" />
    14     </Path.Data>
    15 </Path>

    代码的重点在加粗的数字部分,为保证代码简洁,结构清晰,去掉了渐变色的处理,后面再加上。上述代码的效果如下图:

    3、表盘刻度绘制

    对于表盘刻度,是由许多直线段组成,同样可以用XAML语言绘制出来。但是这样,代码量有点大,同时我们也要手动输入许多坐标值,方法很笨,完全没有发挥出C#的功力。下面我先用XAML语言写出一个刻度(小刻度),以说明原理,然后用C#语言在后台绘出所有刻度,这样便于后期代码的维护和仪表盘的个性化定做。在20度角的直线刻度两个坐标的计算如下图所示。直线刻度的起点是在圆心为(200,200),半径为180的圆上;终点是在圆心为(200,200),半径为170的圆上。

    根据上述计算出的结果,写出直线的XAML语言的代码和效果如下:

    <Line Stroke="Green" StrokeThickness="2" X1="30.85533" Y1="138.43637" X2="40.25225" Y2="141.85658"/>

    1.小刻度绘制

    有了上面的基础知识,所有的小刻度可以很容易绘制出来,先看C#的后台代码:

     1 public MainWindow()
     2 {
     3     InitializeComponent();
     4     this.DrawScale();
     5 }
     6 /// <summary>
     7 /// 画表盘的刻度
     8 /// </summary>
     9 private void DrawScale()
    10 {
    11     for (int i = 0; i <= 180; i += 5)
    12     {
    13         //添加刻度线
    14         Line lineScale = new Line();
    15 
    16         lineScale.Stroke = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0));//使用红色的线
    17         lineScale.StrokeThickness = 1;//线条的粗细为1
    18         //直线刻度的起点,注意角度转为弧度制
    19         lineScale.X1 = 200 - 170 * Math.Cos(i * Math.PI / 180);
    20         lineScale.Y1 = 200 - 170 * Math.Sin(i * Math.PI / 180);
    21         //直线刻度的终点,注意角度转为弧度制
    22         lineScale.X2 = 200 - 180 * Math.Cos(i * Math.PI / 180);
    23         lineScale.Y2 = 200 - 180 * Math.Sin(i * Math.PI / 180);
    24         //将直线画在Canvas画布上
    25         this.gaugeCanvas.Children.Add(lineScale);
    26     }
    27 }

    同样,代码的关键点还是在第19,20,22,23行,180-170=10,这个10表示的就是小刻度的长度。画出所有刻度后,效果如下:

    2.大刻度绘制

    大刻度是每5个小刻度就出现一次,长度为20px,有了画小刻度的基础,实现在刻度非常容易,只需对DrawScale函数稍加修改,如下面的粗体代码所示

     1         private void DrawScale()
     2         {
     3             for (int i = 0; i <= 180; i += 5)
     4             {
     5                 //添加刻度线
     6                 Line lineScale = new Line();
     7 
     8                 if (i % 25 == 0)//说明已经画了5个小刻度了,加一个大刻度
     9                 {
    10                     lineScale.X1 = 200 - 160 * Math.Cos(i * Math.PI / 180);
    11                     lineScale.Y1 = 200 - 160 * Math.Sin(i * Math.PI / 180);
    12                     lineScale.Stroke = new SolidColorBrush(Color.FromRgb(0x00, 0xFF, 0));
    13                     lineScale.StrokeThickness = 3;
    14                 }
    15                 else
    16                 {
    17                     lineScale.X1 = 200 - 170 * Math.Cos(i * Math.PI / 180);
    18                     lineScale.Y1 = 200 - 170 * Math.Sin(i * Math.PI / 180);
    19                     lineScale.Stroke = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0));
    20                     lineScale.StrokeThickness = 1;
    21                 }
    22                 //直线刻度的终点,注意角度转为弧度制
    23                 lineScale.X2 = 200 - 180 * Math.Cos(i * Math.PI / 180);
    24                 lineScale.Y2 = 200 - 180 * Math.Sin(i * Math.PI / 180);
    25                 //将直线画在Canvas画布上
    26                 this.gaugeCanvas.Children.Add(lineScale);
    27             }
    28         }

    最终实现的效果如下图所示,已经越来越接近了。

    4、表盘刻度值添加

    刻度值是用文本块表示的(TextBlock控件)。控制好文本块在表盘中的坐标就行,实现也很容易,这里有要注意的一点是,由于所有控件是的坐标起点是以左上角为零点,当角度超过90度的时候,坐标应当有所补偿。直接说可能说不清楚,在代码中理解,依旧是对DrawScale()函数进行修改如下:

     1 private void DrawScale()
     2 {
     3     for (int i = 0; i <= 180; i += 5)
     4     {
     5         //添加刻度线
     6         Line lineScale = new Line();
     7 
     8         if (i % 25 == 0)
     9         {
    10             lineScale.X1 = 200 - 160 * Math.Cos(i * Math.PI / 180);
    11             lineScale.Y1 = 200 - 160 * Math.Sin(i * Math.PI / 180);
    12             lineScale.Stroke = new SolidColorBrush(Color.FromRgb(0x00, 0xFF, 0));
    13             lineScale.StrokeThickness = 3;
    14 
    15             //添加刻度值
    16             TextBlock txtScale = new TextBlock();
    17             txtScale.Text = (i).ToString();
    18             txtScale.FontSize = 10;
    19             if (i <= 90)//对坐标值进行一定的修正
    20             {
    21                 Canvas.SetLeft(txtScale, 200 - 155 * Math.Cos(i * Math.PI / 180));
    22             }
    23             else
    24             {
    25                 Canvas.SetLeft(txtScale, 190 - 155 * Math.Cos(i * Math.PI / 180));
    26             }
    27             Canvas.SetTop(txtScale, 200 - 155 * Math.Sin(i * Math.PI / 180));
    28             this.gaugeCanvas.Children.Add(txtScale);
    29         }
    30         else
    31         {
    32             lineScale.X1 = 200 - 170 * Math.Cos(i * Math.PI / 180);
    33             lineScale.Y1 = 200 - 170 * Math.Sin(i * Math.PI / 180);
    34             lineScale.Stroke = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0));
    35             lineScale.StrokeThickness = 1;
    36         }
    37 
    38         lineScale.X2 = 200 - 180 * Math.Cos(i * Math.PI / 180);
    39         lineScale.Y2 = 200 - 180 * Math.Sin(i * Math.PI / 180);
    40 
    41         this.gaugeCanvas.Children.Add(lineScale);
    42     }
    43 }

    添加刻度值后的效果如下图

    5、指针绘制

    做表盘的指针,可以有很多方案,网上有许多人说,用图片代替,但是图片一旦放大后,就会变得模糊,因此,我还是自己动手,做了一个简单的指针,同样是采用Path方法,使用路径绘图,XAML代码如下,指针主要由2条直线和一根弧组成,使用了橙色填充。

     1 <Path x:Name="indicatorPin" Fill="Orange">
     2     <Path.Data>
     3         <PathGeometry>
     4             <PathGeometry.Figures>
     5                 <PathFigure StartPoint="200,195" IsClosed="True">
     6                     <PathFigure.Segments>
     7                         <LineSegment Point="20,200"/>
     8                         <LineSegment Point="200,205"/>
     9                     </PathFigure.Segments>
    10                 </PathFigure>
    11             </PathGeometry.Figures>
    12         </PathGeometry>
    13     </Path.Data>
    14 </Path>

     很多表盘中间有一个指示数值的,这里,我也用一个文本块来仿制一下,XAML语言如下,注意,文本块的位置要分配好。

    <TextBlock x:Name="currentValueTxtBlock" FontSize="20" Canvas.Left="140" Canvas.Top="150"/>

     最终外观如下图所示:

    6、让指针转起来

    指针的转动,很明显,是以(200,200)为圆心,各种角度转动,使用了RotateTransform和DoubleAnimation实现转动动画。动画的时间长度根据角度大小分配,1度8个毫秒。转动的角度大小目前是随机生成的,在此,我将转动动画写在canvas_MouseDown事件里面。C#代码如下:

     1 private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
     2 {
     3     RotateTransform rt = new RotateTransform();
     4     rt.CenterX = 200;
     5     rt.CenterY = 200;
     6 
     7     this.indicatorPin.RenderTransform = rt;
     8 
     9     angelCurrent = angleNext;
    10     Random random = new Random();
    11     angleNext = random.Next(180);
    12 
    13     double timeAnimation = Math.Abs(angelCurrent - angleNext) * 8;
    14     DoubleAnimation da = new DoubleAnimation(angelCurrent, angleNext, new Duration(TimeSpan.FromMilliseconds(timeAnimation)));
    15     da.AccelerationRatio = 1;
    16     rt.BeginAnimation(RotateTransform.AngleProperty, da);

    最终效果如下,终于做完了,享受一下成果!!

    总结心得

    此表盘目前虽然很简单,但是自己一步一步思考然后做出来的,后面如果需要添加定制更加强大的功能,相信得心应手的。第一次写博客,比想像中的难多了,感觉很多东西都难以表述清楚。同时为了更好的表达效果,用了visio制图,和MathType公式编辑器,还用了录屏软件录制窗口视频,然后用迅雷看看截出gif图,最后将gif图处理一下,发布至博客里面,着实不容易,相信后面会越来越容易的。

    同时,下一目标,将此表盘美化和封装成用户控件,供项目调用。

    下下一目标,制作图表控件,敬请关注!!

  • 相关阅读:
    制作dos启动u盘
    服务器之ECC报错检查
    shc 对 Linux shell 脚本加密.
    Linux
    windows查看端口占用
    python语言
    AppScan9.0安装破解
    局域网灰色设置解除
    shell脚本
    nginx安装
  • 原文地址:https://www.cnblogs.com/endlesscoding/p/6667570.html
Copyright © 2011-2022 走看看