以前在百度寫的文檔,轉移到此處
前些天在做NetAnalyzer時,需要使用一個指針儀表,網上看了一下,也有人做過,但是大部分都是收費的,本着自力更生的原則,于是決定自己設計一個,今天拿出來有讀者分享一下。
首先是截圖:
該儀表是以控件形式提供
在開始之前還要贅述一點關于GDI+中角度的計算
如下圖
在WinForm中左上角的點位(0,0),即原點,而其起始角則是圖中劃線處開始的,即為 rad=0;
在繪圖時,尤其是做過扇形統計圖的人應該比較清楚。
--------------------------------------------------------
接下來就是正式開始
首先建立控件,設定為witdth=height=150 ,可以自己定義,我在這裡時可以自适應的
将背景顔色設定為Transparent(透明色),友善以後使用時減少幹擾
在該儀表中主要分為兩部分:背景部分(外框,刻度,機關等一些列基本不需要頻繁變化的部分),前景部分(指針部分)
是以為了不是兩個圖層不互相影響,我們将背景繪制在控件的BackgroundImage 屬性上,而指針部分則需要一個pictrueBox控件作為載體。
首先畫背景
在繪制背景時,又分為外框、刻度,指針固定中心等
1 // 繪制背景 用來總體控制背景的繪制
2 private void DrawBackImg()
3 {
4 Bitmap bit = new Bitmap(this.Width, this.Height);
5 Graphics gp = Graphics.FromImage(bit);
6 gp.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
7 #region 在這裡可以擴充需要繪制的背景項目
8 //外框
9 drawFrame(gp);
10 // 畫刻度
11 DrawRuling(gp);
12 //畫點
13 drawPoint(gp);
14
15 //繪制機關
16
17 DrawUnitStr(gp);
18
19 #endregion
20
21 //當繪制完成後,直接直接設定為背景
22
23 this.BackgroundImage = bit;
24 }
25
26
27 //繪制機關
28
29 private void DrawUnitStr(Graphics gp)
30 {
31 int cer = _diameter / 2;
32 gp.DrawString(_unitStr, new Font("宋體", 10), new SolidBrush(_frameColor), new PointF(cer, (float)(cer - cer * 0.3)), strFormat);
33
34 }
35
36 /// <summary>
37 /// 畫外框
38 /// </summary>
39 /// <param name="gp"></param>
40 private void drawFrame(Graphics gp)
41 {
42 Pen pen = new Pen(_frameColor, 2);
43 Rectangle rec = new Rectangle(5, 5, _diameter - 10, _diameter - 10);
44 gp.DrawEllipse(pen, rec);
45 }
46 // 畫刻度 此次較為複雜,主要是在繪制刻度值時需要處理
47 private void DrawRuling(Graphics gp)
48 {
49 //刻度
50 int cerX = _diameter / 2;
51 int cerY = _diameter / 2;
52
53 //這裡需要注意,因外在上面的圖中辨別了rad=0的位置,而我們的儀表時270度的,0點在135度處,
54
55 //為了符合該效果是以起始位置設為135度。
56 float start = 135;
57 float sweepShot = 0;
58 int dx = 0;
59 int dy = 0;
60 int soildLenght = 8;
61 Pen linePen = new Pen(_frameColor, 1);
62 float span = (float)(_maxValue / 30);
63 float sp = 0;
64 //用于右邊數字右對齊
65 StringFormat stf = new StringFormat();
66 stf.Alignment = StringAlignment.Far;
67
68 StringFormat stfMid = new StringFormat();
69 stfMid.Alignment = StringAlignment.Center;
70 stfMid.LineAlignment = StringAlignment.Center;
71 for (int i = 0; i <= 30; i++)
72 {
73 //注意此處,C#提供的三角函數計算中使用的弧度值,而此處擷取的是角度值,需要轉化
74
75 double rad = (sweepShot + start) * Math.PI / 180;
76 float radius = _diameter / 2 - 5;
77 int px = (int)(cerX + radius * Math.Cos(rad));
78 int py = (int)(cerY + radius * Math.Sin(rad));
79 if (sweepShot % 15 == 0)
80 {
81 linePen.Width = 2;
82
83 //計算刻度中的粗線
84 dx = (int)(cerX + (radius - soildLenght) * Math.Cos(rad));
85 dy = (int)(cerY + (radius - soildLenght) * Math.Sin(rad));
86
87 //繪制刻度值,注意字串對其方式
88 string str = sp.ToString("f0");
89 if (sweepShot <= 45)
90 {
91 gp.DrawString(str, new Font("宋體", 9), new SolidBrush(_frameColor), new PointF(dx, dy - 5));
92 }
93 else if (sweepShot > 45 && sweepShot < 135)
94 {
95 gp.DrawString(str, new Font("宋體", 9), new SolidBrush(_frameColor), new PointF(dx, dy));
96 }
97 else if (sweepShot == 135)
98 {
99 gp.DrawString(str, new Font("宋體", 9), new SolidBrush(_frameColor), new PointF(dx, dy + 10), stfMid);
100 }
101 else if (sweepShot > 135 && sweepShot < 225)
102 {
103 gp.DrawString(str, new Font("宋體", 9), new SolidBrush(_frameColor), new PointF(dx, dy), stf);
104 }
105 else if (sweepShot >= 225)
106 {
107 gp.DrawString(str, new Font("宋體", 9), new SolidBrush(_frameColor), new PointF(dx, dy - 5), stf);
108 }
109
110 }
111 else
112 {
113
114 //計算刻度中細線
115
116 linePen.Width = 1;
117 dx = (int)(cerX + (radius - soildLenght + 2) * Math.Cos(rad));
118 dy = (int)(cerY + (radius - soildLenght + 2) * Math.Sin(rad));
119 }
120
121 //繪制刻度線
122 gp.DrawLine(linePen, new Point(px, py), new Point(dx, dy));
123 sp += span;
124 sweepShot += 9;
125 }
126 }
127 //畫中間的點
128 private void drawPoint(Graphics gp)
129 {
130 Pen p = new Pen(_frameColor);
131 int tmpWidth = 6;
132 int px = _diameter / 2 - tmpWidth;
133
134 gp.DrawEllipse(p, new Rectangle(px, px, 2 * tmpWidth, 2 * tmpWidth));
135
136 //在畫點時,我使用了指針的顔色,這樣看起來,更真實一點
137 gp.FillEllipse(new SolidBrush(_pinColor), new Rectangle(px + 2, px + 2, 2 * tmpWidth - 4, 2 * tmpWidth - 4));
138 }
139
140 -------------------------------------------
141
142 畫指針
143
144 繪制指正時,最大的問題就是界面閃速,除了在控件構造方法裡添加如下代碼:
145
146 SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true);
147 UpdateStyles();
148
149 繪制方式也需要調整,方法如下:
150
151
152
153 //為了方式繪制指針時産生的閃爍,PictureBox添加該事件方法
154
155 private void pic_Paint(object sender, PaintEventArgs e)
156 {
157 DrawForeImg(e.Graphics);
158 }
159
160 //使用方法
161
162 public double ChangeValue
163 {
164 get { return _changeValue; }
165 set
166 {
167 if (value <= _maxValue)
168 _changeValue = value;
169 else
170 {
171 //完成自适應性
172 MaxValue = value;
173 _changeValue = value;
174 }
175 //通過該方法,可以使指針自動繪制(其實就是強制重繪)
176
177 pic.Invalidate();
178 }
179 }
180
181 //指針的具體畫法
182
183 private void DrawForeImg(Graphics gp)
184 {
185 Bitmap bit = new Bitmap(this.Width, this.Height);
186 Graphics g = Graphics.FromImage(bit);
187 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
188
189 //畫針
190 DrawPin(g);
191 DrawString(g);
192
193 //注意此處的繪制方式,這樣可以有效減少界面的閃爍問題。
194 gp.DrawImage(bit, new Point(0, 0));
195 g.Dispose();
196
197 }
198 //畫針
199 private void DrawPin(Graphics g)
200 {
201 int cer = _diameter / 2;
202 float start = 135;
203 float sweepShot = (float)(_changeValue / _maxValue * 270);
204
205 Pen linePen = new Pen(_pinColor, 1);
206 Pen NxPen = new Pen(_pinColor, 2);
207 Pen xPen = new Pen(_pinColor, 5);
208 double rad = (sweepShot + start) * Math.PI / 180;
209 float radius = _diameter / 2 - 5;
210 int dx = (int)(cer + (_PinLen) * Math.Cos(rad));
211 int dy = (int)(cer + (_PinLen) * Math.Sin(rad));
212
213 int px = (int)(cer + (_PinLen * 0.4) * Math.Cos(rad));
214 int py = (int)(cer + (_PinLen * 0.4) * Math.Sin(rad));
215
216 int nx = (int)(cer - (NxPinLen) * Math.Sin(rad));
217 int ny = (int)(cer - (NxPinLen) * Math.Cos(rad));
218 g.DrawLine(linePen, new Point(cer, cer), new Point(dx, dy));
219 g.DrawLine(NxPen, new Point(cer, cer), new Point(px, py));
220 g.DrawLine(xPen, new Point(cer, cer), new Point(ny, nx));
221 }
222
223 //繪制在儀表下面的值
224
225 private void DrawString(Graphics g)
226 {
227 int cer = _diameter / 2;
228 string str = _changeValue.ToString("F2");
229 g.DrawString(str, new Font("宋體", 9), new SolidBrush(_pinColor), new PointF(cer, (float)(cer + cer * 0.4)), strFormat);
230 }
NetAnalyzer下載下傳位址
NetAnalzyer交流群:39753670 (PS 隻提供交流平台,群主基本不說話^_^)
[轉載請保留作者資訊 作者:馮天文 ]