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圖處理一下,發布至博客里面,著實不容易,相信后面會越來越容易的。
同時,下一目標,將此表盤美化和封裝成用戶控件,供項目調用。
下下一目標,制作圖表控件,敬請關注!!
轉載于:https://www.cnblogs.com/endlesscoding/p/6667570.html
總結
以上是生活随笔為你收集整理的WPF自定义控件(1)——仪表盘设计[1]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring-boot-maven-pl
- 下一篇: fullcaledar日历插件