WPF自定义控件(1)——仪表盘设计[1]

0、小叙闲言

又接一个新的类了,再来平等蹩脚上位机开发。网上发成百上千控件库,做仪表盘(gauge)的也罢未丢掉,功能吗很强劲,但是个人认为库很臃肿,自己不怕计划入手来形容一个控件库,一凡是啊念,二凡是为着项目。下面是自家花了平下午底时空举行出来的,先押效能: 

图片 1
其一表盘当前还比丑,后面会同样步一步地全面它的,包括各种美化,相信自己力所能及就的,加油!!这为是自家个人第一不行写博客,我会坚持下去,同时为会见大力表述清楚各国一个技术细节。源码地址:https://github.com/Endless-Coding/MyGauge/blob/master/CustomControl.zip

1、表盘总体设计

 一个表面,就大概来拘禁,应该由四单部分构成,即:表盘外轮廓、刻度(包括小刻度和大刻度)、刻度值、指针。在打造的历程被,略微用了有数学知识,只要用思想考,都十分易之。设计外观的进程被,用到了对诺如下知识点。当然也席卷一些C#同WPF的基础知识,如果来不知情的地方,可以省刘铁猛先生的《深入浅出WPF》

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

2、表盘外轮廓

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

图片 2

确定性好拘留出来,这个圆由三段弧组成的,如果观察仔细的言辞,可以隐约看到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渡过,黄色点的坐标计算如下图。

图片 3

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>

代码的首要以加粗的数字有,为力保代码简洁,结构清晰,去丢了日益变色的拍卖,后面更加上。上述代码的力量使下图:

图片 4

3、表盘刻度绘制

于表盘刻度,是出于众直线段组成,同样可以用XAML语言绘制出。但是这么,代码量有接触杀,同时我们为使手动输入过多以标值,方法很笨,完全没有发表出C#的功。下面我先用XAML语言写有一个刻度(小刻度),以说明原理,然后用C#语言在后台绘出所有刻度,这样有利于后期代码的维护与仪表盘的天性化定做。在20度比的直线刻度两只坐标的精打细算如下图所示。直线刻度的起点是以圆心为(200,200),半径为180底圆上;终点是于圆心为(200,200),半径为170之圆上。

图片 5

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

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

图片 6

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象征的就是是有点刻度的长短。画来富有刻度后,效果如下:

图片 7

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         }

末尾兑现之职能使下图所著,已经越来越接近了。

图片 8

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 }

补加刻度值后的职能使下图

图片 9

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"/>

 最终外观如下图所示:

图片 10

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

最终效果如下,终于做扫尾了,享受一下收获!!

图片 11

总心得

其一表盘目前虽说老简短,但是自己平步一步思考然后开下的,后面要用丰富定制越来越有力的意义,相信得心应手的。第一次写博客,比想像吃的不便多了,感觉很多物都难以发挥清楚。同时为重新好的达效果,用了visio制图,和MathType公式编辑器,还用了录屏软件录制窗口视频,然后据此迅雷看看截出gif图,最后以gif图处理一下,发布到博客里,着实无轻,相信后面会愈爱的。

再就是,下同样对象,将之表盘美化和封装成用户控件,供项目调用。

下下一对象,制作图纸控件,敬请关注!!