天天看點

C# 制作 儀表

以前在百度寫的文檔,轉移到此處

前些天在做NetAnalyzer時,需要使用一個指針儀表,網上看了一下,也有人做過,但是大部分都是收費的,本着自力更生的原則,于是決定自己設計一個,今天拿出來有讀者分享一下。

首先是截圖:

C# 制作 儀表

該儀表是以控件形式提供

在開始之前還要贅述一點關于GDI+中角度的計算

如下圖

C# 制作 儀表

在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 隻提供交流平台,群主基本不說話^_^)

[轉載請保留作者資訊  作者:馮天文 ]