C# 報表設計器 (winform 設計端)開發與實現生成網頁的HTML報表,
記得2010年之前,公司的項目基本上都要用到報表,以前我們常用的方法就是針對客戶的需求來定制化開發(基本上是死寫代碼)來實現,經常導致項目經常性的延期,因為客戶的需求經常會變化,隨著用戶的使用認知度的提高,對報表的要求越來越高,導致程序員不停的修改代碼來實現,效率不高、結束遙遙無期。。。非常的痛苦;當然市面上有很多報表開發工具可以實現,但是針對小公司來說采購一套這樣的系統的成本也非常的高,所以我們決定自己來開發一套像目前的潤乾、FineReport 這樣的報表設計器,來實現快速的報表設計制作。
當初為了開發這樣的系統,花費的了很長的時間學習查閱各種資料,其痛苦只有程序員才能體會,因為沒有任何現成的實例代碼可供參考,只有看別人的思路來一步步的摸索,本文將我們當初設計制作的報表設計器的功能分享出來,讓有需要的或想開發報表設計的朋友們提供一個參考,盡量少走很動彎路,設計端可以直接使用,但是計算引擎和網頁的計算的源碼就不能分享出來了(請不要介意,因為涉及到公司的保密原因)
記得當初為了制作報表設計器,在網上查找有沒有相關的實例資料,找了很久,也是沒有找到合適的,後來發現 SourceGrid 可以實現單元格的合並拆分功能,所以決定修改實現winform端的報表設計。下面我將制作的E_Report 報表控件抽取出來建立一個簡易的Winform的可運行的實例提供給大伙下載,希望能給你的開發提供一點幫助和借鑒;當然你可以直接使用也可以,裡面的設計功能基本全部能。
抽取出來的源碼包含:E_Report 報表設計自定義控件DLL源碼; EReportDemo 建立的簡易Winform 端設計器使用DLL的實例源碼;
一、運行效果
實例中,只做了一個簡單的效果,工具欄的按鈕在單元格右擊屬性中都有,只是放了幾個常用的在工具導航欄中(右擊單元格屬性可以看到設計導航)
可以進行單元格的合並、拆分、字體、顏色、背景、邊框等的設置,朋友們可以自己編寫保存發布等功能,實現報表的真實功能;

例如單元格屬性(其他還有很多的屬性,自己下載源碼後運行起來就能看到了)

對表格的斜線、斜線文字有很好的支持;可以設置表頭、表位、標題等 實際效果圖如下

二、使用介紹
1、頁面初始化的時候,通過 ReportDoc 類 初始報表的行列及單元格屬性

![]()
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Drawing;
5 using System.Windows.Forms;
6 using System.Drawing.Drawing2D;
7 using System.Xml;
8 using System.Collections;
9 using E_Report;
10
11 namespace EReportDemo
12 {
13 /// <summary>
14 /// 報表表格對象
15 /// </summary>
16 public class ReportDoc
17 {
18 #region 變量參數定義
19
20 /// <summary>
21 /// 表格對象
22 /// </summary>
23 private EReport _nowReport;
24
25 /// <summary>
26 /// 報表配置編碼
27 /// </summary>
28 private string _reportCode = "";
29
30 /// <summary>
31 /// 表報設計狀態
32 /// </summary>
33 private string _reportState = "";
34
35 #endregion
36
37 #region 函數構造方法
38
39 /// <summary>
40 /// 構造函數
41 /// </summary>
42 public ReportDoc()
43 {
44 this._nowReport = null;
45 this._reportCode = "";
46 this._reportState = "";
47
48 }
49
50
51 /// <summary>
52 /// 獲取--設置--表格對象
53 /// </summary>
54 public EReport NowReport
55 {
56 get { return this._nowReport; }
57 set { this._nowReport = value; }
58 }
59
60 /// <summary>
61 /// 報表配置編碼
62 /// </summary>
63 public string ReportCode
64 {
65 get { return this._reportCode; }
66 set { this._reportCode = value; }
67 }
68
69 /// <summary>
70 /// 報表設計狀態
71 /// 新增、修改 兩種狀態
72 /// </summary>
73 public string ReportState
74 {
75 get { return this._reportState; }
76 set { this._reportState = value; }
77 }
78
79 /// <summary>
80 /// 資源釋放
81 /// </summary>
82 ~ReportDoc()
83 {
84 this._nowReport = null;
85 this._reportState = "";
86
87 }
88
89 #endregion
90
91 #region 加載報表表格
92
93 /// <summary>
94 /// 初始化--報表表格
95 /// </summary>
96 public void InitReport()
97 {
98
99 int rCount = 41; // 41行
100 int cCount = 20; // 20列
101
102 _nowReport.Redim(rCount, cCount);
103 _nowReport.FixedRows = 1;
104 _nowReport.FixedColumns = 1;
105
106 InitCells();
107
108 }
109
110 /// <summary>
111 /// 初始化--單元格
112 /// </summary>
113 public void InitCells()
114 {
115 // 第一行 第一列
116 _nowReport.Rows[0].Height = 23;
117 _nowReport.Columns[0].Width = 50;
118
119 // 設置00格
120 _nowReport[0, 0] = new E_Report.Cells.HeaderColumn("");
121
122 // 設置行
123 for (int rr = 1; rr < _nowReport.RowsCount; rr++)
124 {
125 string tmRowT = rr.ToString();
126 _nowReport[rr, 0] = new E_Report.Cells.HeaderRow(tmRowT);
127 }
128
129 // 設置列
130 for (int cc = 1; cc < _nowReport.ColumnsCount; cc++)
131 {
132 _nowReport[0, cc] = new E_Report.Cells.HeaderColumn(_nowReport.GetColumnHeadTileChar(cc));
133 }
134
135 // 設置單元格
136 for (int iRow = 1; iRow < _nowReport.RowsCount; iRow++)
137 {
138 for (int iCol = 1; iCol < _nowReport.ColumnsCount; iCol++)
139 {
140 _nowReport[iRow, iCol] = new E_Report.Cells.Cell("", typeof(string));
141 }
142 }
143
144 }
145
146
147 #endregion
148
149 }
150 }
View Code
2、工具導航欄 設置單元格相關屬性

![]()
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Text;
7 using System.Windows.Forms;
8 using System.Collections;
9 using E_Report;
10
11 namespace EReportDemo
12 {
13 /// <summary>
14 /// 本程序只是Winform端的報表設計功能
15 /// 至於其他的功能,本實例沒有提供
16 /// 報表設計的設計效果:可以查看 www.sdpsoft.com SDP軟件快速開發平台 報表設計篇
17 /// </summary>
18
19 public partial class EReportMain : Form
20 {
21 private ReportDoc report;
22 private E_Report.Cells.Controllers.PopupMenu myPopupMenu;
23
24 public EReportMain()
25 {
26 InitializeComponent();
27 }
28
29 private void EReportMain_Load(object sender, EventArgs e)
30 {
31 Cursor.Current = Cursors.WaitCursor;
32 gridMain.Rows.Clear();
33 myPopupMenu = new E_Report.Cells.Controllers.PopupMenu(gridMain);
34
35 report = new ReportDoc();
36 report.NowReport = gridMain;
37 report.InitReport();
38 Cursor.Current = Cursors.Default;
39 }
40
41 private void gridMain_MouseMove(object sender, MouseEventArgs e)
42 {
43 this.lbl_X.Text = e.X.ToString();
44 this.lbl_Y.Text = e.Y.ToString();
45 }
46
47 /// <summary>
48 /// 工具欄報表單元格事件
49 /// </summary>
50 /// <param name="sender"></param>
51 /// <param name="e"></param>
52 private void btn_GridTools_Click(object sender, EventArgs e)
53 {
54 string sType = ((Button)sender).Tag.ToString().Trim().ToLower();
55 switch (sType)
56 {
57
58 case "cellproperty": // 單元格屬性設置
59 myPopupMenu.CellProperty_Click(sender, e);
60 break;
61 case "fontset": // 單元格字體設置
62 myPopupMenu.CellFont_Click(sender, e);
63 break;
64 case "fontcolor": // 文本字體顏色
65 myPopupMenu.CellForColor_Click(sender, e);
66 break;
67 case "backcolor": // 單元格背景色
68 myPopupMenu.CellBackColor_Click(sender, e);
69 break;
70 case "cellborder": // 單元格邊框設置
71 myPopupMenu.CellBorder_Click(sender, e);
72 break;
73 case "lock": // 設置表格只讀
74 myPopupMenu.LockReport_Click(sender, e);
75 break;
76 case "unlock": // 設置表格編輯
77 myPopupMenu.UnLockReport_Click(sender, e);
78 break;
79 case "alignleft": // 水平居左對齊
80 myPopupMenu.AlignLeft_Click(sender, e);
81 break;
82 case "aligncenter": // 水平居中對齊
83 myPopupMenu.AlignCenter_Click(sender, e);
84 break;
85 case "alignright": // 水平居右對齊
86 myPopupMenu.AlignRight_Click(sender, e);
87 break;
88 case "aligntop": // 垂直居上對齊
89 myPopupMenu.AlignTop_Click(sender, e);
90 break;
91 case "alignmiddle": // 垂直居中對齊
92 myPopupMenu.AlignMiddle_Click(sender, e);
93 break;
94 case "alignbottom": // 垂直居下對齊
95 myPopupMenu.AlignBottom_Click(sender, e);
96 break;
97 case "addindent": // 增加文本縮進
98 myPopupMenu.AddIndent_Click(sender, e);
99 break;
100 case "delindent": // 清除文本縮進
101 myPopupMenu.RemoveIndent_Click(sender, e);
102 break;
103 case "insertrow": // 插入後一行
104 myPopupMenu.InsertRow_Click(sender, e);
105 break;
106 case "appendrow": // 表格追加行
107 myPopupMenu.AddRow_Click(sender, e);
108 break;
109 case "delrow": // 刪除選中行
110 myPopupMenu.DeleteRows_Click(sender, e);
111 break;
112 case "hiderow": // 隱藏選中行
113 myPopupMenu.HideSelectRows_Click(sender, e);
114 break;
115 case "showrow": // 顯示選中行
116 myPopupMenu.ShowSelectRows_Click(sender, e);
117 break;
118 case "showallrow": // 顯示所有行
119 myPopupMenu.ShowAllRows_Click(sender, e);
120 break;
121 case "insertcol": // 插入左側列
122 myPopupMenu.InsertColumn_Click(sender, e);
123 break;
124 case "addcol": // 插入右側列
125 myPopupMenu.AddColumn_Click(sender, e);
126 break;
127 case "delcol": // 刪除選中列
128 myPopupMenu.DeleteColumns_Click(sender, e);
129 break;
130 case "hidecol": // 隱藏選中列
131 myPopupMenu.HideSelectColumns_Click(sender, e);
132 break;
133 case "showcol": // 顯示選中列
134 myPopupMenu.ShowSelectColumns_Click(sender, e);
135 break;
136 case "showallcol": // 顯示所有列
137 myPopupMenu.ShowAllColumns_Click(sender, e);
138 break;
139 case "mergecell": // 合並單元格
140 myPopupMenu.MergeCell_Click(sender, e);
141 break;
142 case "splitcell": // 拆分單元格
143 myPopupMenu.SplitCell_Click(sender, e);
144 break;
145 }
146 }
147 }
148 }
View Code
3、報表控件DLL類庫部分
裡面有我們自定義的 條碼控件、圖片控件、圖表控件

表格內自定義圖表控件(曲線、柱狀、餅狀)源碼

![]()
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Drawing;
5 using System.Drawing.Drawing2D;
6 using System.Drawing.Text;
7
8 namespace E_Report
9 {
10 /// <summary>
11 /// 圖表屬性
12 /// </summary>
13 /// <summary>
14 /// 報表圖表類庫
15 /// </summary>
16 public class EChart
17 {
18 #region 屬性方法
19
20 #region 臨時變量
21
22 /// <summary>
23 /// 臨時變量--關聯單元格行號
24 /// </summary>
25 private int _row = 0;
26
27 /// <summary>
28 /// 獲取--設置--關聯單元格行號
29 /// </summary>
30 public int Row
31 {
32 get { return _row; }
33 set { _row = value; }
34 }
35
36 /// <summary>
37 /// 臨時變量--關聯單元格列號
38 /// </summary>
39 private int _col = 0;
40
41 /// <summary>
42 /// 獲取--設置--關聯單元格列號
43 /// </summary>
44 public int Col
45 {
46 get { return _col; }
47 set { _col = value; }
48 }
49
50 /// <summary>
51 /// 數據來源
52 /// </summary>
53 private string _t_DataFrom = "數據源";
54
55 /// <summary>
56 /// 獲取--設置--數據來源
57 /// </summary>
58 public string T_DataFrom
59 {
60 get { return _t_DataFrom; }
61 set { _t_DataFrom = value; }
62 }
63
64 /// <summary>
65 /// 數據源名稱
66 /// </summary>
67 private string _t_DsName = "";
68
69 /// <summary>
70 /// 獲取--設置--數據源名稱
71 /// </summary>
72 public string T_DsName
73 {
74 get { return _t_DsName; }
75 set { _t_DsName = value; }
76 }
77
78 /// <summary>
79 /// 項目名稱
80 /// </summary>
81 private string _t_ItemName = "";
82
83 /// <summary>
84 /// 獲取--設置--項目名稱
85 /// </summary>
86 public string T_ItemName
87 {
88 get { return _t_ItemName; }
89 set { _t_ItemName = value; }
90 }
91
92 /// <summary>
93 /// 項目數值
94 /// </summary>
95 private string _t_ItemValue = "";
96
97 /// <summary>
98 /// 獲取--設置--項目數值
99 /// </summary>
100 public string T_ItemValue
101 {
102 get { return _t_ItemValue; }
103 set { _t_ItemValue = value; }
104 }
105
106 /// <summary>
107 /// X軸刻度
108 /// </summary>
109 private string _t_XScale = "";
110
111 /// <summary>
112 /// 獲取--設置--X軸刻度
113 /// </summary>
114 public string T_XScale
115 {
116 get { return _t_XScale; }
117 set { _t_XScale = value; }
118 }
119
120
121 #endregion
122
123 #region 圖表屬性
124
125 /// <summary>
126 /// 圖表名稱ID
127 /// </summary>
128 private string _name = "";
129
130 /// <summary>
131 /// 獲取--設置--圖表名稱ID
132 /// </summary>
133 public string Name
134 {
135 get { return _name; }
136 set { _name = value; }
137 }
138
139 /// <summary>
140 /// 圖表類型
141 /// </summary>
142 private EChartType _chartType = EChartType.Curve;
143
144 /// <summary>
145 /// 獲取--設置--圖表類型
146 /// 默認:Line 折曲線
147 /// </summary>
148 public EChartType ChartType
149 {
150 get { return _chartType; }
151 set { _chartType = value; }
152 }
153
154 /// <summary>
155 /// 圖表寬度
156 /// </summary>
157 private int _chartWidth = 0;
158
159 /// <summary>
160 /// 獲取--設置--圖表寬度
161 /// </summary>
162 public int ChartWidth
163 {
164 get { return _chartWidth; }
165 set { _chartWidth = value; }
166 }
167
168 /// <summary>
169 /// 圖表高度
170 /// </summary>
171 private int _chartHeight = 0;
172
173 /// <summary>
174 /// 獲取--設置--圖表高度
175 /// </summary>
176 public int ChartHeight
177 {
178 get { return _chartHeight; }
179 set { _chartHeight = value; }
180 }
181
182 /// <summary>
183 /// 圖表背景顏色
184 /// </summary>
185 private Color _backColor = Color.White;
186
187 /// <summary>
188 /// 獲取--設置--圖表背景顏色
189 /// </summary>
190 public Color BackColor
191 {
192 get { return _backColor; }
193 set { _backColor = value; }
194 }
195
196 /// <summary>
197 /// 是否顯示圖表邊框
198 /// </summary>
199 private bool _showBorder = true;
200
201 /// <summary>
202 /// 獲取--設置--是否顯示圖表邊框
203 /// 默認:true 顯示
204 /// </summary>
205 public bool ShowBorder
206 {
207 get { return _showBorder; }
208 set { _showBorder = value; }
209 }
210
211 /// <summary>
212 /// 圖表邊框顏色
213 /// </summary>
214 private Color _borderColor = Color.LightGray;
215
216 /// <summary>
217 /// 獲取--設置--圖表邊框顏色
218 /// </summary>
219 public Color BorderColor
220 {
221 get { return _borderColor; }
222 set { _borderColor = value; }
223 }
224
225 /// <summary>
226 /// 是否顯示網格線
227 /// </summary>
228 private bool _showGrid = true;
229
230 /// <summary>
231 /// 獲取--設置--是否顯示網格線
232 /// 默認:true 顯示
233 /// </summary>
234 public bool ShowGrid
235 {
236 get { return _showGrid; }
237 set { _showGrid = value; }
238 }
239
240 /// <summary>
241 /// 線條張力系數
242 /// </summary>
243 private float _lineTension = 0.3f;
244
245 /// <summary>
246 /// 獲取--設置--線條張力系數
247 /// 默認:0.3
248 /// </summary>
249 public float LineTension
250 {
251 get { return _lineTension; }
252 set
253 {
254 if (value < 0.0f && value > 1.0f)
255 _lineTension = 0.5f;
256 else
257 _lineTension = value;
258 }
259 }
260
261 #endregion
262
263 #region 標題屬性
264
265 /// <summary>
266 /// 是否顯示主標題
267 /// </summary>
268 private bool _showTitle = true;
269
270 /// <summary>
271 /// 獲取--設置--是否顯示主標題
272 /// 默認:true 顯示
273 /// </summary>
274 public bool ShowTitle
275 {
276 get { return _showTitle; }
277 set { _showTitle = value; }
278 }
279
280 /// <summary>
281 /// 主標題文本
282 /// </summary>
283 private string _title = "";
284
285 /// <summary>
286 /// 獲取--設置--主標題文本
287 /// </summary>
288 public string Title
289 {
290 get { return _title; }
291 set { _title = value; }
292 }
293
294 /// <summary>
295 /// 主標題字體
296 /// </summary>
297 private Font _titleFont = new Font("黑體", 12);
298
299 /// <summary>
300 /// 獲取--設置--主標題字體
301 /// </summary>
302 public Font TitleFont
303 {
304 get { return _titleFont; }
305 set { _titleFont = value; }
306 }
307
308 /// <summary>
309 /// 主標題顏色
310 /// </summary>
311 private Color _titleColor = Color.Black;
312
313 /// <summary>
314 /// 獲取--設置--主標題顏色
315 /// </summary>
316 public Color TitleColor
317 {
318 get { return _titleColor; }
319 set { _titleColor = value; }
320 }
321
322 /// <summary>
323 /// 主標題對齊方式
324 /// </summary>
325 private EAlign _titleAlign = EAlign.center;
326
327 /// <summary>
328 /// 獲取--設置--主標題對齊方式
329 /// </summary>
330 public EAlign TitleAlign
331 {
332 get { return _titleAlign; }
333 set { _titleAlign = value; }
334 }
335
336 /// <summary>
337 /// 是否顯示副標題
338 /// </summary>
339 private bool _showSubTilte = true;
340
341 /// <summary>
342 /// 獲取--設置--是否顯示副標題
343 /// 默認:true 顯示
344 /// </summary>
345 public bool ShowSubTitle
346 {
347 get { return _showSubTilte; }
348 set { _showSubTilte = value; }
349 }
350
351 /// <summary>
352 /// 副標題文本
353 /// </summary>
354 private string _subTitle = "";
355
356 /// <summary>
357 /// 獲取--設置--副標題文本
358 /// </summary>
359 public string SubTitle
360 {
361 get { return _subTitle; }
362 set { _subTitle = value; }
363 }
364
365 /// <summary>
366 /// 副標題字體
367 /// </summary>
368 private Font _subTitleFont = new Font("黑體", 10);
369
370 /// <summary>
371 /// 獲取--設置--副標題字體
372 /// </summary>
373 public Font SubTitleFont
374 {
375 get { return _subTitleFont; }
376 set { _subTitleFont = value; }
377 }
378
379 /// <summary>
380 /// 副標題顏色
381 /// </summary>
382 private Color _subTitleColor = Color.Blue;
383
384 /// <summary>
385 /// 獲取--設置--副標題顏色
386 /// </summary>
387 public Color SubTitleColor
388 {
389 get { return _subTitleColor; }
390 set { _subTitleColor = value; }
391 }
392
393 /// <summary>
394 /// 副標題對齊方式
395 /// </summary>
396 private EAlign _subTitleAlign = EAlign.center;
397
398 /// <summary>
399 /// 獲取--設置--副標題對齊方式
400 /// </summary>
401 public EAlign SubTitleAlign
402 {
403 get { return _subTitleAlign; }
404 set { _subTitleAlign = value; }
405 }
406
407 /// <summary>
408 /// 副標題水平方向偏移量
409 /// + - 整數
410 /// </summary>
411 private int _subTitleOffset = 0;
412
413 /// <summary>
414 /// 獲取--設置--副標題水平方向偏移量
415 /// </summary>
416 public int SubTitleOffset
417 {
418 get { return _subTitleOffset; }
419 set { _subTitleOffset = value; }
420 }
421
422 #endregion
423
424 #region 圖例屬性
425
426 /// <summary>
427 /// 是否顯示圖例
428 /// </summary>
429 private bool _showLegend = true;
430
431 /// <summary>
432 /// 是否顯示圖例
433 /// </summary>
434 public bool ShowLegend
435 {
436 get { return _showLegend; }
437 set { _showLegend = value; }
438 }
439
440 /// <summary>
441 /// 圖例字體
442 /// </summary>
443 private Font _legendFont = new Font("宋體", 9);
444
445 /// <summary>
446 /// 獲取--設置--圖例字體
447 /// </summary>
448 public Font LegendFont
449 {
450 get { return _legendFont; }
451 set { _legendFont = value; }
452 }
453
454 /// <summary>
455 /// 圖例字體顏色
456 /// </summary>
457 private Color _legendColor = Color.Black;
458
459 /// <summary>
460 /// 獲取--設置--圖例字體顏色
461 /// </summary>
462 public Color LegendColor
463 {
464 get { return _legendColor; }
465 set { _legendColor = value; }
466 }
467
468 /// <summary>
469 /// 圖例對齊方式
470 /// </summary>
471 private EAlign _legendAlign = EAlign.center;
472
473 /// <summary>
474 /// 獲取--設置--圖例對齊方式
475 /// </summary>
476 public EAlign LegendAlign
477 {
478 get { return _legendAlign; }
479 set { _legendAlign = value; }
480 }
481
482 #endregion
483
484 #region 坐標屬性
485
486 /// <summary>
487 /// 獲取--X軸分割段數
488 /// </summary>
489 public int XSplitNum
490 {
491 get { return _xScaleValues.Count; }
492 }
493
494 /// <summary>
495 /// Y軸分割段數
496 /// </summary>
497 private int _ySplitNum = 5;
498
499 /// <summary>
500 /// 獲取--設置--分割段數
501 /// 默認:5
502 /// 范圍:最小5
503 /// </summary>
504 public int YSplitNum
505 {
506 get { return _ySplitNum; }
507 set
508 {
509 if (value < 5)
510 _ySplitNum = 5;
511 else
512 _ySplitNum = value;
513
514 }
515 }
516
517 /// <summary>
518 /// X 軸標
519 /// </summary>
520 private string _xAxisText = "";
521
522 /// <summary>
523 /// 獲取--設置--X 軸標
524 /// </summary>
525 public string XAxisText
526 {
527 get { return _xAxisText; }
528 set { _xAxisText = value; }
529 }
530
531 /// <summary>
532 /// Y 軸標
533 /// </summary>
534 private string _yAxisText = "";
535
536 /// <summary>
537 /// 獲取--設置--Y 軸標
538 /// </summary>
539 public string YAxisText
540 {
541 get { return _yAxisText; }
542 set { _yAxisText = value; }
543 }
544
545 /// <summary>
546 /// X軸文本旋轉角度
547 /// 默認:0
548 /// 范圍:0~90
549 /// </summary>
550 private float _xRotateAngle = 0.0f;
551
552 /// <summary>
553 /// 獲取--設置--X軸文本旋轉角度
554 /// </summary>
555 public float XRotateAngle
556 {
557 get { return _xRotateAngle; }
558 set
559 {
560 if (value >= 0.0f && value <= 90.0f)
561 _xRotateAngle = value;
562 else
563 _xRotateAngle = 0.0f;
564 }
565 }
566
567 #endregion
568
569 #region 繪圖變量
570
571 /// <summary>
572 /// 繪圖對象
573 /// </summary>
574 private Graphics g = null;
575
576 /// <summary>
577 /// 圖表顏色數組
578 /// </summary>
579 private static Color[] ChartColor = {
580 Color.Red, Color.Blue, Color.Orange, Color.Green, Color.Cyan, Color.Purple,
581 Color.Coral, Color.Chocolate, Color.Gray, Color.Gold, Color.Lavender, Color.Linen,
582 Color.Magenta, Color.Moccasin, Color.Navy, Color.Olive, Color.Peru, Color.Plum,
583 Color.Purple, Color.Salmon, Color.Sienna, Color.Silver, Color.Tan, Color.Tomato,
584 Color.Violet, Color.Turquoise, Color.Transparent
585 };
586
587 /// <summary>
588 /// 邊距10px
589 /// </summary>
590 private float Margin = 10;
591
592 /// <summary>
593 /// 起點 X 坐標
594 /// </summary>
595 private float Start_X = 0;
596
597 /// <summary>
598 /// 起點 Y 坐標
599 /// </summary>
600 private float Start_Y = 0;
601
602 /// <summary>
603 /// 終點 X 坐標
604 /// </summary>
605 private float End_X = 0;
606
607 /// <summary>
608 /// 終點 Y 坐標
609 /// </summary>
610 private float End_Y = 0;
611
612 /// <summary>
613 /// X軸刻度寬度
614 /// </summary>
615 private float XScaleWidth = 0;
616
617 /// <summary>
618 /// Y軸刻度寬度
619 /// </summary>
620 private float YScaleWidth = 0;
621
622 /// <summary>
623 /// Y軸刻度間隔值
624 /// 說明:Y軸坐標全部采用整數,表示每個間隔的計算單位值
625 /// 包含正負數
626 /// </summary>
627 private double YScale_SplitValue = 0;
628
629 /// <summary>
630 /// Y軸刻度開始值
631 /// </summary>
632 private double YScale_StartValue = 0;
633
634 /// <summary>
635 /// 坐標軸原點坐標
636 /// </summary>
637 private PointF AxisZeroPt = new PointF(0f, 0f);
638
639 /// <summary>
640 /// 圖表數據
641 /// </summary>
642 private string _chartData = "";
643
644 /// <summary>
645 /// 獲取--設置--圖表數據
646 /// </summary>
647 public string ChartData
648 {
649 get { return _chartData; }
650 set { _chartData = value; }
651 }
652
653 /// <summary>
654 /// 繪圖筆刷
655 /// </summary>
656 private Brush brush;
657
658 /// <summary>
659 /// 繪制畫筆
660 /// </summary>
661 private Pen pen;
662
663 /// <summary>
664 /// 繪圖矩形
665 /// </summary>
666 private RectangleF rectF = new RectangleF(0, 0, 0, 0);
667
668 /// <summary>
669 /// 字符格式化
670 /// </summary>
671 private StringFormat stringFormat;
672
673 /// <summary>
674 /// 臨時變量 最大值
675 /// </summary>
676 private double myMaxValue = 0;
677
678 /// <summary>
679 /// 臨時變量 最小值
680 /// </summary>
681 private double myMinValue = 0;
682
683 /// <summary>
684 /// 臨時變量 X軸刻度最大高度
685 /// 用於繪制坐標軸的時候進行偏移
686 /// </summary>
687 private float myXScaleMaxHeight = 0;
688
689 /// <summary>
690 /// 臨時變量 Y軸刻度值字符串的最大寬度
691 /// 用於繪制坐標軸的時候進行偏移
692 /// </summary>
693 private float myYScaleMaxWidth = 0;
694
695 #endregion
696
697 #region 圖表數據
698
699 /// <summary>
700 /// X軸刻度值數組
701 /// </summary>
702 private List<string> _xScaleValues = new List<string>();
703
704 /// <summary>
705 /// 獲取--設置--X軸刻度值數組
706 /// </summary>
707 public List<string> XScaleValues
708 {
709 get { return _xScaleValues; }
710 set { _xScaleValues = value; }
711 }
712
713 /// <summary>
714 /// 圖表數據
715 /// </summary>
716 private List<EChartData> _chartDataArray = new List<EChartData>();
717
718 /// <summary>
719 /// 獲取--設置--圖表數據
720 /// </summary>
721 public List<EChartData> ChartDataArray
722 {
723 get { return _chartDataArray; }
724 set { _chartDataArray = value; }
725 }
726
727 #endregion
728
729 #endregion
730
731 #region 構造方法
732
733 /// <summary>
734 /// 構造函數
735 /// </summary>
736 public EChart()
737 {
738
739 }
740
741 /// <summary>
742 /// 構造函數
743 /// </summary>
744 /// <param name="eChart"></param>
745 public EChart(EChart eChart)
746 {
747
748 }
749
750 #endregion
751
752 #region 生成圖表
753
754 /// <summary>
755 /// 生成圖表
756 /// </summary>
757 /// <returns>返回:圖表圖像</returns>
758 public Bitmap CreateImage()
759 {
760 Bitmap ChartImage = new Bitmap(ChartWidth, ChartHeight);
761 g = Graphics.FromImage(ChartImage);
762 g.SmoothingMode = SmoothingMode.Default;
763 g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
764 g.Clear(Color.White);
765
766 Start_X = Margin;
767 Start_Y = Margin;
768 End_X = ChartWidth - Margin;
769 End_Y = ChartHeight - Margin;
770
771 DrawChart();
772
773 g.Dispose();
774
775 return ChartImage;
776 }
777
778 #endregion
779
780 #region 繪制方法
781
782 /// <summary>
783 /// 繪制圖表
784 /// </summary>
785 private void DrawChart()
786 {
787 DrawChart_InitGraph();
788 DrawChart_MainTitle();
789 DrawChart_SubTitle();
790 DrawChart_Legend();
791 DrawChart_YAxisText();
792 if (ChartType != EChartType.Pie)
793 {
794 DrawChart_Axis();
795 if (ChartType == EChartType.Curve)
796 DrawChart_Curve();
797 else
798 DrawChart_Bar();
799 }
800 else
801 {
802 DrawChart_Pie();
803 }
804 }
805
806 /// <summary>
807 /// 繪制圖表--繪制背景圖
808 /// </summary>
809 private void DrawChart_InitGraph()
810 {
811 // 繪制圖表外圍邊框
812 if (_showBorder)
813 g.DrawRectangle(new Pen(BorderColor, 1), 0, 0, (ChartWidth - 1), (ChartHeight - 1));
814
815 // 填充圖表背景顏色
816 g.FillRectangle(new SolidBrush(BackColor), 1, 1, ChartWidth - 2, ChartHeight - 2);
817
818 }
819
820 /// <summary>
821 /// 繪制圖表--繪制主標題
822 /// </summary>
823 private void DrawChart_MainTitle()
824 {
825 if (ShowTitle)
826 {
827 if (Title != null && Title.Trim() != "")
828 {
829 brush = new SolidBrush(TitleColor); // 矩形填充筆刷
830 SizeF sizeF = g.MeasureString(Title, TitleFont); // 測試字體大小
831 stringFormat = new StringFormat(); // 格式化字符串
832 stringFormat.LineAlignment = StringAlignment.Center; // 垂直對齊方式
833 switch (TitleAlign) // 水平對齊方式
834 {
835 case EAlign.center:
836 stringFormat.Alignment = StringAlignment.Center;
837 break;
838 case EAlign.right:
839 stringFormat.Alignment = StringAlignment.Far;
840 break;
841 default:
842 stringFormat.Alignment = StringAlignment.Near;
843 break;
844 }
845
846 rectF = new RectangleF(Start_X, Start_Y, (float)(ChartWidth - 4), sizeF.Height); // 文字的矩形
847 g.DrawString(Title, TitleFont, brush, rectF, stringFormat); // 繪制主標題
848 Start_Y += sizeF.Height + 3f; // 設置Y起點值 + 3px
849 }
850 }
851 }
852
853 /// <summary>
854 /// 繪制圖表--繪制副標題
855 /// </summary>
856 private void DrawChart_SubTitle()
857 {
858 if (ShowSubTitle)
859 {
860 if (SubTitle != null && SubTitle.Trim() != "")
861 {
862 brush = new SolidBrush(SubTitleColor); // 矩形填充筆刷
863 SizeF sizeF = g.MeasureString(SubTitle, SubTitleFont); // 測試字體大小
864 stringFormat = new StringFormat(); // 格式化字符串
865 stringFormat.LineAlignment = StringAlignment.Center; // 垂直對齊方式
866 switch (SubTitleAlign) // 水平對齊方式
867 {
868 case EAlign.center:
869 stringFormat.Alignment = StringAlignment.Center;
870 break;
871 case EAlign.right:
872 stringFormat.Alignment = StringAlignment.Far;
873 break;
874 default:
875 stringFormat.Alignment = StringAlignment.Near;
876 break;
877 }
878
879 rectF = new RectangleF(Start_X + (float)SubTitleOffset, Start_Y, (float)(ChartWidth - 4), sizeF.Height); // 文字的矩形
880 g.DrawString(SubTitle, SubTitleFont, brush, rectF, stringFormat); // 繪制副標題
881 Start_Y += sizeF.Height + 3f; // 設置Y起點值 + 3px
882 }
883 }
884
885 }
886
887 /// <summary>
888 /// 繪制圖表--繪制圖例
889 /// </summary>
890 private void DrawChart_Legend()
891 {
892 // 計算項目顏色
893 int tmIndex = 0;
894 for (int m = 0; m < ChartDataArray.Count; m++)
895 {
896 tmIndex = m % ChartColor.Length;
897 ChartDataArray[m].ItemColor = ChartColor[tmIndex];
898 }
899
900 // 圖例的高度最大40px 3排
901 if (ShowLegend)
902 {
903 // 計算文字大小
904 int LegendCount = ChartDataArray.Count;
905 Font tFont = new Font("宋體", 9);
906 for (int t = 0; t < LegendCount; t++)
907 {
908 SizeF tmSize = new SizeF();
909 tmSize = g.MeasureString(ChartDataArray[t].Name, tFont);
910 ChartDataArray[t].NameSize = tmSize;
911 }
912
913 #region 繪制一排圖例
914
915 // 首先判定一行夠不夠
916 float largMax = 0;
917 for (int t = 0; t < LegendCount; t++)
918 {
919 if (t == 0)
920 largMax += ChartDataArray[t].NameSize.Width;
921 else
922 largMax += (35f + ChartDataArray[t].NameSize.Width);
923 }
924
925 if (largMax <= End_X - Start_X) // 圖例只需一排
926 {
927 End_Y -= 12.0f;
928 float tmX = (End_X - Start_X - largMax) / 2 + Start_X;
929 float tmY = End_Y;
930 for (int n = 0; n < LegendCount; n++)
931 {
932 g.FillRectangle(new SolidBrush(ChartDataArray[n].ItemColor), tmX, tmY + 1, 15, 10);
933 tmX += 20;
934 g.DrawString(ChartDataArray[n].Name, new Font("宋體", 9), new SolidBrush(Color.Black), tmX, tmY);
935 tmX += 15 + ChartDataArray[n].NameSize.Width;
936 }
937 }
938
939 #endregion
940
941 #region 繪制多排圖例
942
943 // 圖例最多繪制三排
944 else
945 {
946
947 bool TwoLine = true; // 是否兩行:true 是; false: 否,為三行
948
949 // 判定兩排還是三排
950 float tmBX = Start_X - 5;
951 int tmBC = (int)Math.Ceiling((double)LegendCount / 2);
952 for (int T = 0; T < tmBC; T++)
953 {
954 float tmBW1 = -1F, tmBW2 = -1F, tmBM = 0F;
955 tmBW1 = ChartDataArray[T * 2].NameSize.Width;
956 if (ChartDataArray.Count > (T * 2 + 1))
957 tmBW2 = ChartDataArray[T * 2 + 1].NameSize.Width;
958 tmBM = tmBW1 > tmBW2 ? tmBW1 : tmBW2;
959 tmBX += 35 + tmBM;
960 if (tmBX > (End_X + 5))
961 {
962 TwoLine = false;
963 break;
964 }
965 }
966
967 // 繪制兩排圖例
968 if (TwoLine)
969 {
970 End_Y -= 24.0f;
971 float tmTX = (End_X + 10 - tmBX + Start_X + 5) / 2; // 開始位置保持兩排水平居中
972 float tmTY = End_Y;
973 int tmTM = (int)Math.Ceiling((double)LegendCount / 2);
974
975 // 繪制兩排圖例
976 for (int T = 0; T < tmTM; T++)
977 {
978 float tmTW1 = -1F, tmTW2 = -1F, tmTW3 = 0F;
979 tmTW1 = ChartDataArray[T * 2].NameSize.Width;
980 if (ChartDataArray.Count > (T * 2 + 1))
981 tmTW2 = ChartDataArray[T * 2 + 1].NameSize.Width;
982 tmTW3 = tmTW1 > tmTW2 ? tmTW1 : tmTW2;
983
984 // 繪制第一排圖例
985 g.FillRectangle(new SolidBrush(ChartDataArray[T * 2].ItemColor), tmTX, tmTY + 1, 15, 10);
986 g.DrawString(ChartDataArray[T * 2].Name, new Font("宋體", 9), new SolidBrush(Color.Black), (tmTX + 20), tmTY);
987
988 // 繪制第二排圖例
989 if (tmTW2 > 0)
990 {
991 g.FillRectangle(new SolidBrush(ChartDataArray[T * 2 + 1].ItemColor), tmTX, (tmTY + 16 + 1), 15, 10);
992 g.DrawString(ChartDataArray[T * 2 + 1].Name, new Font("宋體", 9), new SolidBrush(Color.Black), (tmTX + 20), (tmTY + 16));
993 }
994 tmTX += 35 + tmTW3;
995 }
996 }
997
998 // 繪制三排圖例
999 else
1000 {
1001 End_Y -= 40.0f;
1002 // 如果三排還不夠,那麼就不管了,繪制超出范圍就超出范圍
1003 float tmSX = Start_X - 5;
1004 float tmSY = End_Y;
1005 int tmSC = (int)Math.Ceiling((double)LegendCount / 3);
1006 bool CanFlag = true; // 三排是否足夠
1007
1008 // 首先計算三排的能排下的居中位置
1009 for (int n = 0; n < tmSC; n++)
1010 {
1011 float tmSW1 = -1F, tmSW2 = -1F, tmSW3 = -1F, tmSW4 = 0F;
1012 tmSW1 = ChartDataArray[n * 3].NameSize.Width;
1013 if (ChartDataArray.Count > (n * 3 + 1))
1014 tmSW2 = ChartDataArray[n * 3 + 1].NameSize.Width;
1015 if (ChartDataArray.Count > (n * 3 + 2))
1016 tmSW3 = ChartDataArray[n * 3 + 2].NameSize.Width;
1017 tmSW4 = tmSW1 > tmSW2 ? tmSW1 : tmSW2;
1018 tmSW4 = tmSW4 > tmSW3 ? tmSW4 : tmSW3;
1019 tmSX += 35 + tmSW4;
1020 if (tmSX > (End_X + 5))
1021 {
1022 CanFlag = false;
1023 break;
1024 }
1025 }
1026
1027 // 再次執行三排繪制
1028 if (CanFlag) // 三排足夠,則設置居中開始位置
1029 tmSX = (End_X + 10 - tmSX + Start_X + 5) / 2;
1030 else
1031 tmSX = Start_X - 5; // 三排排不下的情況就從5px 開始
1032
1033 for (int n = 0; n < tmSC; n++)
1034 {
1035 float tmSW1 = -1F, tmSW2 = -1F, tmSW3 = -1F, tmSW4 = 0F;
1036 tmSW1 = ChartDataArray[n * 3].NameSize.Width;
1037 if (ChartDataArray.Count > (n * 3 + 1))
1038 tmSW2 = ChartDataArray[n * 3 + 1].NameSize.Width;
1039 if (ChartDataArray.Count > (n * 3 + 2))
1040 tmSW3 = ChartDataArray[n * 3 + 2].NameSize.Width;
1041 tmSW4 = tmSW1 > tmSW2 ? tmSW1 : tmSW2;
1042 tmSW4 = tmSW4 > tmSW3 ? tmSW4 : tmSW3;
1043
1044 // 繪制第一排圖例
1045 g.FillRectangle(new SolidBrush(ChartDataArray[n * 3].ItemColor), tmSX, (tmSY + 1), 15, 10);
1046 g.DrawString(ChartDataArray[n * 3].Name, new Font("宋體", 9), new SolidBrush(Color.Black), (tmSX + 20), tmSY);
1047
1048 // 繪制第二排圖例
1049 if (tmSW2 > 0)
1050 {
1051 g.FillRectangle(new SolidBrush(ChartDataArray[n * 3 + 1].ItemColor), tmSX, (tmSY + 16 + 1), 15, 10);
1052 g.DrawString(ChartDataArray[n * 3 + 1].Name, new Font("宋體", 9), new SolidBrush(Color.Black), (tmSX + 20), (tmSY + 16));
1053 }
1054
1055 // 繪制第三排圖例
1056 if (tmSW3 > 0)
1057 {
1058 g.FillRectangle(new SolidBrush(ChartDataArray[n * 3 + 2].ItemColor), tmSX, (tmSY + 32 + 1), 15, 10);
1059 g.DrawString(ChartDataArray[n * 3 + 2].Name, new Font("宋體", 9), new SolidBrush(Color.Black), (tmSX + 20), (tmSY + 32));
1060 }
1061
1062 tmSX += 35 + tmSW4;
1063 }
1064 }
1065 }
1066
1067 #endregion
1068 }
1069 }
1070
1071 /// <summary>
1072 /// 繪制圖表--繪制X軸標
1073 /// </summary>
1074 /// <param name="g"></param>
1075 private void DrawChart_XAxisText()
1076 {
1077 // X軸標就不繪制了,因為空間不夠,所以不執行X軸標的繪制
1078 }
1079
1080 /// <summary>
1081 /// 繪制圖表--繪制Y軸標
1082 /// </summary>
1083 private void DrawChart_YAxisText()
1084 {
1085 if (ChartType != EChartType.Pie)
1086 {
1087 if (YAxisText != null && YAxisText.Trim() != "")
1088 {
1089 brush = new SolidBrush(Color.Gray);
1090 stringFormat = new StringFormat(); // 格式化字符串
1091 stringFormat.LineAlignment = StringAlignment.Near; // 垂直對齊方式
1092 stringFormat.Alignment = StringAlignment.Near; // 水平對齊方式
1093 SizeF sizeF = g.MeasureString(YAxisText, new Font("宋體", 9)); // 測量文字大小
1094 rectF = new RectangleF(Start_X, Start_Y, sizeF.Width, sizeF.Height); // 文字外圍矩形
1095 g.TranslateTransform((Start_X - Start_Y), (Start_X + Start_Y + sizeF.Width)); // 設置位置移動 X,Y
1096 g.RotateTransform(270); // 旋轉270度 以左上角作為旋轉原點
1097 g.DrawString(YAxisText, new Font("宋體", 9), brush, rectF, stringFormat);
1098 g.ResetTransform();
1099
1100 Start_X += sizeF.Height + 2; // 加2個像素
1101 }
1102 }
1103 }
1104
1105 /// <summary>
1106 /// 繪制圖表--繪制坐標軸
1107 /// </summary>
1108 private void DrawChart_Axis()
1109 {
1110 // 1、圖表區下移10PX
1111 Start_Y += 10;
1112
1113 // 2、計算坐標軸參數
1114 Calc_XScaleHeight();
1115 Calc_YScaleValue();
1116
1117 // 3、計算原點坐標值
1118 AxisZeroPt = new PointF(0f, 0f); // 坐標軸原點坐標
1119 AxisZeroPt.X = Start_X + myYScaleMaxWidth;
1120 AxisZeroPt.Y = End_Y - myXScaleMaxHeight;
1121
1122 // 3.1、繪制坐標軸
1123 Pen pen2 = new Pen(Color.FromKnownColor(KnownColor.ControlDark), 2f);
1124 g.DrawLine(pen2, (Start_X + myYScaleMaxWidth - 4f), (End_Y - myXScaleMaxHeight), End_X + 2, (End_Y - myXScaleMaxHeight)); // 繪制 X坐標軸
1125 g.DrawLine(pen2, (Start_X + myYScaleMaxWidth), (End_Y - myXScaleMaxHeight + 4f), (Start_X + myYScaleMaxWidth), Start_Y - 2); // 繪制 Y坐標軸
1126
1127 // 3.2、計算分段寬
1128 XScaleWidth = (End_X - Start_X - myYScaleMaxWidth) / XSplitNum; // 計算X軸分段寬
1129 YScaleWidth = (End_Y - Start_Y - myXScaleMaxHeight) / YSplitNum; // 計算Y軸分段寬
1130
1131 // 3.3、繪制刻度值
1132 pen = new Pen(Color.LightGray, 1f);
1133 pen.DashStyle = DashStyle.Dash;
1134 for (int k = 0; k < XSplitNum; k++) // 繪制X軸刻度線 刻度值
1135 {
1136 // 繪制X軸刻度線
1137 g.DrawLine(pen2, (AxisZeroPt.X + XScaleWidth * (k + 1)), AxisZeroPt.Y, (AxisZeroPt.X + XScaleWidth * (k + 1)), AxisZeroPt.Y + 4f);
1138
1139 // 繪制X軸刻度值
1140 g.TranslateTransform(AxisZeroPt.X + XScaleWidth * k + 15f * XRotateAngle / 90 + 4f, AxisZeroPt.Y + 4f); // 平移原點
1141 g.RotateTransform(XRotateAngle, MatrixOrder.Prepend); // 旋轉圖像
1142 g.DrawString(XScaleValues[k], new Font("宋體", 9f), new SolidBrush(Color.Black), 0, 0); // 繪制字符
1143 g.ResetTransform();
1144 }
1145
1146 for (int k = 0; k < YSplitNum; k++)
1147 {
1148 // 繪制Y軸刻度線
1149 g.DrawLine(pen2, AxisZeroPt.X - 4, (AxisZeroPt.Y - YScaleWidth * (k + 1)), AxisZeroPt.X, (AxisZeroPt.Y - YScaleWidth * (k + 1)));
1150
1151 // 繪制Y軸刻度值
1152 string tmYvalue = (YScale_StartValue + k * YScale_SplitValue).ToString();
1153 SizeF tmF = g.MeasureString(tmYvalue, new Font("宋體", 9));
1154 g.DrawString(tmYvalue, new Font("宋體", 9), new SolidBrush(Color.Black), (AxisZeroPt.X - tmF.Width - 4), (AxisZeroPt.Y - YScaleWidth * k - tmF.Height / 2 + 1));
1155
1156 if (k == YSplitNum - 1)
1157 {
1158 tmYvalue = (YScale_StartValue + (k + 1) * YScale_SplitValue).ToString();
1159 tmF = g.MeasureString(tmYvalue, new Font("宋體", 9));
1160 g.DrawString(tmYvalue, new Font("宋體", 9), new SolidBrush(Color.Black), (AxisZeroPt.X - tmF.Width - 4), (AxisZeroPt.Y - YScaleWidth * (k + 1) - tmF.Height / 2 + 1));
1161 }
1162 }
1163
1164
1165 // 3.4、繪制網格線
1166 if (ShowGrid)
1167 {
1168 for (int k = 1; k <= YSplitNum; k++) // 繪制X軸平行橫向輔助線
1169 g.DrawLine(pen, AxisZeroPt.X + 1, (AxisZeroPt.Y - YScaleWidth * k), End_X, (AxisZeroPt.Y - YScaleWidth * k));
1170
1171 for (int k = 1; k <= XSplitNum; k++) // 繪制Y軸平行縱向輔助線
1172 g.DrawLine(pen, (AxisZeroPt.X + XScaleWidth * k), Start_Y, (AxisZeroPt.X + XScaleWidth * k), AxisZeroPt.Y - 1);
1173 }
1174
1175 pen2.Dispose();
1176 }
1177
1178 /// <summary>
1179 /// 繪制曲線圖
1180 /// </summary>
1181 private void DrawChart_Curve()
1182 {
1183 g.SmoothingMode = SmoothingMode.HighQuality;
1184 g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
1185 g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
1186
1187 int tmLength = 0;
1188 PointF[] CurvePointF = new PointF[tmLength];
1189 foreach (EChartData iItem in this.ChartDataArray)
1190 {
1191 tmLength = iItem.Values.Count;
1192 CurvePointF = new PointF[tmLength];
1193 pen = new Pen(iItem.ItemColor, 2.0f);
1194 for (int rr = 0; rr < iItem.Values.Count; rr++)
1195 {
1196 double dbValue = iItem.Values[rr];
1197 CurvePointF[rr].X = AxisZeroPt.X + XScaleWidth * rr + XScaleWidth / 2;
1198 CurvePointF[rr].Y = AxisZeroPt.Y - (float)((dbValue - YScale_StartValue) / YScale_SplitValue) * YScaleWidth;
1199 }
1200
1201 // 繪制曲線
1202 g.DrawCurve(pen, CurvePointF, LineTension);
1203
1204 // 繪制輔點
1205 for (int tt = 0; tt < CurvePointF.Length; tt++)
1206 {
1207 // 點標數據值
1208 string tmValStr = iItem.Values[tt].ToString();
1209
1210 // 繪制數據點
1211 g.FillEllipse(new SolidBrush(iItem.ItemColor), CurvePointF[tt].X - 3, CurvePointF[tt].Y - 3, 6, 6);
1212
1213 // 繪制數據值
1214 SizeF tmValueSize = g.MeasureString(tmValStr, new Font("Arial", 9));
1215 g.DrawString(tmValStr, new Font("Arial", 9), new SolidBrush(iItem.ItemColor), (CurvePointF[tt].X - tmValueSize.Width / 2), (CurvePointF[tt].Y - tmValueSize.Height - 2f));
1216 }
1217 }
1218 }
1219
1220 /// <summary>
1221 /// 繪制柱狀圖
1222 /// </summary>
1223 private void DrawChart_Bar()
1224 {
1225 g.SmoothingMode = SmoothingMode.HighQuality;
1226 int tmLen = ChartDataArray.Count; // 柱形條目總數
1227 float tmBarWidth = XScaleWidth / (tmLen * 2 + 1); // 每條柱形寬度 平均分配
1228 if (tmBarWidth < 2)
1229 {
1230 tmBarWidth = 2f;
1231 }
1232
1233 for (int kk = 0; kk < this.ChartDataArray.Count; kk++)
1234 {
1235 EChartData iItem = this.ChartDataArray[kk];
1236 pen = new Pen(Color.FromKnownColor(KnownColor.ControlDark), 1.0f);
1237 for (int rr = 0; rr < iItem.Values.Count; rr++)
1238 {
1239 RectangleF barRect = new RectangleF(0, 0, 0, 0);
1240 double dbValue = iItem.Values[rr];
1241 barRect.X = AxisZeroPt.X + XScaleWidth * rr + (tmBarWidth * ((kk + 1) * 2 - 1));
1242 barRect.Y = AxisZeroPt.Y - (float)((dbValue - YScale_StartValue) / YScale_SplitValue) * YScaleWidth;
1243 barRect.Width = tmBarWidth;
1244 barRect.Height = AxisZeroPt.Y - barRect.Y;
1245
1246 // 繪制柱形
1247 g.DrawRectangle(pen, barRect.X, barRect.Y, barRect.Width, barRect.Height);
1248
1249 brush = new SolidBrush(iItem.ItemColor);
1250 g.FillRectangle(brush, barRect.X + 1, barRect.Y + 1, barRect.Width - 2, barRect.Height - 2);
1251
1252 // 繪制數據
1253 SizeF tmValueSize = g.MeasureString(dbValue.ToString(), new Font("Arial", 9));
1254 g.DrawString(dbValue.ToString(), new Font("Arial", 9), new SolidBrush(iItem.ItemColor), (barRect.X + tmBarWidth / 2 - tmValueSize.Width / 2), (barRect.Y - tmValueSize.Height - 2f));
1255 }
1256 }
1257 }
1258
1259 /// <summary>
1260 /// 繪制圖表--繪制餅狀圖
1261 /// </summary>
1262 private void DrawChart_Pie()
1263 {
1264 // 上下預留20 PX 為了標記餅圖的數據值
1265 Start_Y += 20;
1266 End_Y -= 20;
1267
1268 g.SmoothingMode = SmoothingMode.HighQuality;
1269 g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
1270 g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
1271
1272 // 矩形坐標點
1273 PointF piePoint = new PointF(0, 0);
1274 float tmPieW = End_X - Start_X;
1275 float tmPieH = End_Y - Start_Y;
1276 float tmPieR = tmPieW < tmPieH ? tmPieW / 2 : tmPieH / 2; // 半徑
1277
1278 piePoint.X = Start_X + tmPieW / 2 - tmPieR;
1279 piePoint.Y = Start_Y + tmPieH / 2 - tmPieR;
1280
1281 // 圓心坐標點
1282 PointF pieZero = new PointF(piePoint.X + tmPieR, piePoint.Y + tmPieR);
1283
1284 // 繪制外圍圓
1285 pen = new Pen(Color.FromKnownColor(KnownColor.ControlDark), 1.5f);
1286 g.DrawEllipse(pen, piePoint.X - 5, piePoint.Y - 5, (tmPieR + 5) * 2, (tmPieR + 5) * 2);
1287
1288 // 計算總數
1289 double pieCountValue = 0;
1290 foreach (EChartData iItem in this.ChartDataArray)
1291 {
1292 if (iItem.Values.Count >= 1)
1293 pieCountValue += iItem.Values[0];
1294 }
1295
1296 // 繪制扇形
1297 if (pieCountValue > 0)
1298 {
1299 float curAngle = 0; // 占比角度
1300 float sumAngle = 0; // 總角度和
1301
1302 foreach (EChartData iItem in this.ChartDataArray)
1303 {
1304 if (iItem.Values.Count >= 1)
1305 curAngle = (float)(iItem.Values[0] / pieCountValue * 360);
1306 else
1307 curAngle = 0;
1308
1309 // 填充筆刷
1310 brush = new SolidBrush(iItem.ItemColor);
1311
1312 // 繪制弧形
1313 g.FillPie(brush, piePoint.X, piePoint.Y, tmPieR * 2, tmPieR * 2, sumAngle, curAngle);
1314
1315 // 繪制弧線
1316 g.DrawPie(pen, piePoint.X, piePoint.Y, tmPieR * 2, tmPieR * 2, sumAngle, curAngle);
1317
1318 // 繪制數據
1319 float tmPre = (float)Math.Round((iItem.Values[0] / pieCountValue) * 100, 2);
1320 string tmStr = tmPre.ToString() + "%" + " [" + iItem.Values[0].ToString() + "]";
1321
1322 // 內圓信息
1323 double relCur_X = tmPieR * Math.Cos((360 - sumAngle - curAngle / 2) * Math.PI / 180);
1324 double relCur_Y = tmPieR * Math.Sin((360 - sumAngle - curAngle / 2) * Math.PI / 180);
1325 double cur_X = relCur_X + pieZero.X;
1326 double cur_Y = pieZero.Y - relCur_Y;
1327 PointF cur_Point = new PointF((float)cur_X, (float)cur_Y); // 內圓上弧線中間點的坐標
1328
1329 // 外圓信息
1330 float largerR = tmPieR + 10;
1331 double relLarg_X = largerR * Math.Cos((360 - sumAngle - curAngle / 2) * Math.PI / 180);
1332 double relLarg_Y = largerR * Math.Sin((360 - sumAngle - curAngle / 2) * Math.PI / 180);
1333 double largerX = relLarg_X + pieZero.X;
1334 double largerY = pieZero.Y - relLarg_Y;
1335 PointF larger_Point = new PointF((float)largerX, (float)largerY);
1336
1337 SizeF calcSize = new SizeF(0, 0);
1338
1339 // 繪制鏈接斜線(內圓到外圓弧度中間點的連接線)
1340 g.DrawLine(new Pen(new SolidBrush(iItem.ItemColor), 1.5f), cur_Point, larger_Point); // 斜線
1341
1342 // 繪制橫向線條
1343 //*以下是對四個象限、以及對90度、180度、270度和360度的判斷*//
1344 float tmCurIf = sumAngle + curAngle / 2;
1345 if (tmCurIf <= 90)
1346 {
1347 g.DrawLine(new Pen(new SolidBrush(iItem.ItemColor), 1.5f), larger_Point.X, larger_Point.Y, larger_Point.X + 15f, larger_Point.Y); // 橫線
1348 g.DrawString(tmStr, new Font("Arial", 9), new SolidBrush(iItem.ItemColor), larger_Point.X + 15f, larger_Point.Y - 8f); // 文字
1349 }
1350 else if (tmCurIf > 90 && tmCurIf <= 180)
1351 {
1352 calcSize = g.MeasureString(tmStr, new Font("Arial", 9));
1353 g.DrawLine(new Pen(new SolidBrush(iItem.ItemColor), 1.5f), larger_Point.X, larger_Point.Y, larger_Point.X - 15f, larger_Point.Y); // 橫線
1354 g.DrawString(tmStr, new Font("Arial", 9), new SolidBrush(iItem.ItemColor), larger_Point.X - 15f - calcSize.Width, larger_Point.Y - 8f); // 文字
1355 }
1356 else if (tmCurIf > 180 && tmCurIf <= 270)
1357 {
1358 calcSize = g.MeasureString(tmStr, new Font("Arial", 9));
1359 g.DrawLine(new Pen(new SolidBrush(iItem.ItemColor), 1.5f), larger_Point.X, larger_Point.Y, larger_Point.X - 15f, larger_Point.Y); // 橫線
1360 g.DrawString(tmStr, new Font("Arial", 9), new SolidBrush(iItem.ItemColor), larger_Point.X - 15f - calcSize.Width, larger_Point.Y - 8f); // 文字
1361 }
1362 else
1363 {
1364 g.DrawLine(new Pen(new SolidBrush(iItem.ItemColor), 1.5f), larger_Point.X, larger_Point.Y, larger_Point.X + 15f, larger_Point.Y); // 橫線
1365 g.DrawString(tmStr, new Font("Arial", 9), new SolidBrush(iItem.ItemColor), larger_Point.X + 15f, larger_Point.Y - 8f); // 文字
1366 }
1367
1368 ////*以下是對四個象限、以及對90度、180度、270度和360度的判斷*//
1369 //if ((sumAngle + curAngle / 2) < 90)
1370 //{
1371 // Half = sumAngle + curAngle / 2;
1372 // double tem_sin = Math.Sin(Pi / 180 * Half);
1373 // double tem_cos = Math.Cos(Pi / 180 * Half);
1374
1375 // //Math.PI
1376
1377 // float Px = (float)(tmPieR * tem_cos);
1378 // float Py = (float)(tmPieR * tem_sin);
1379
1380 // g.DrawString(tmStr, new Font("Arial", 9), new SolidBrush(Color.Black), piePoint.X + tmPieR + Px, piePoint.Y + Py);
1381 // //g.DrawString(arraylist_type[i].ToString(), font1, brush1, new Point(225 + x, 225 + y));
1382 // //g.DrawString((Convert.ToInt32(arraylist_tp[i])).ToString() + "%", font1, brush1, new Point(225 + x, 225 + y + 12));
1383 //}
1384
1385 //else if (tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 == 90)
1386 //{
1387 // g.DrawString(arraylist_type[i].ToString(), font1, brush1, new Point(225, 225 + 125));
1388 // g.DrawString((Convert.ToInt32(arraylist_tp[i])).ToString() + "%", font1, brush1, new Point(225, 225 + 125 + 12));
1389 //}
1390
1391 //else if (tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 > 90 && tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 < 180)
1392 //{
1393 // halfangle = (180 - tem_angle - Convert.ToSingle(arraylist_angle[i]) / 2);
1394 // double tem_sin = Math.Sin(T / 180 * halfangle);
1395 // double tem_cos = Math.Cos(T / 180 * halfangle);
1396
1397 // int y = Convert.ToInt32(125 * tem_sin);
1398 // int x = Convert.ToInt32(125 * tem_cos);
1399 // g.DrawString(arraylist_type[i].ToString(), font1, brush1, new Point(225 - x, 225 + y));
1400 // g.DrawString((Convert.ToInt32(arraylist_tp[i])).ToString() + "%", font1, brush1, new Point(225 - x, 225 + y + 12));
1401 //}
1402
1403 //else if (tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 == 180)
1404 //{
1405 // g.DrawString(arraylist_type[i].ToString(), font1, brush1, new Point(225 - 125, 225));
1406 // g.DrawString((Convert.ToInt32(arraylist_tp[i])).ToString() + "%", font1, brush1, new Point(225 - 125, 225 + 12));
1407 //}
1408
1409 //else if (tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 > 180 && tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 < 270)
1410 //{
1411 // halfangle = (tem_angle - 180 + Convert.ToSingle(arraylist_angle[i]) / 2);
1412 // double tem_sin = Math.Sin(T / 180 * halfangle);
1413 // double tem_cos = Math.Cos(T / 180 * halfangle);
1414
1415 // int y = Convert.ToInt32(125 * tem_sin);
1416 // int x = Convert.ToInt32(125 * tem_cos);
1417 // g.DrawString(arraylist_type[i].ToString(), font1, brush1, new Point(225 - x, 225 - y));
1418 // g.DrawString((Convert.ToInt32(arraylist_tp[i])).ToString() + "%", font1, brush1, new Point(225 - x, 225 - y + 12));
1419 //}
1420
1421 //else if (tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 == 270)
1422 //{
1423 // g.DrawString(arraylist_type[i].ToString(), font1, brush1, new Point(225, 225 - 125));
1424 // g.DrawString((Convert.ToInt32(arraylist_tp[i])).ToString() + "%", font1, brush1, new Point(225, 225 - 125 + 12));
1425 //}
1426
1427 //else if (tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 > 270 && tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 < 360)
1428 //{
1429 // halfangle = (360 - tem_angle - Convert.ToSingle(arraylist_angle[i]) / 2);
1430 // double tem_sin = Math.Sin(T / 180 * halfangle);
1431 // double tem_cos = Math.Cos(T / 180 * halfangle);
1432
1433 // int y = Convert.ToInt32(125 * tem_sin);
1434 // int x = Convert.ToInt32(125 * tem_cos);
1435 // g.DrawString(arraylist_type[i].ToString(), font1, brush1, new Point(225 + x, 225 - y));
1436 // g.DrawString((Convert.ToInt32(arraylist_tp[i])).ToString() + "%", font1, brush1, new Point(225 + x, 225 - y + 12));
1437 //}
1438
1439 //else if (tem_angle + Convert.ToSingle(arraylist_angle[i]) / 2 == 360)
1440 //{
1441 // g.DrawString(arraylist_type[i].ToString(), font1, brush1, new Point(225 + 125, 225));
1442 // g.DrawString((Convert.ToInt32(arraylist_tp[i])).ToString() + "%", font1, brush1, new Point(225 + 125, 225 + 12));
1443 //}
1444
1445
1446 // 累加角度
1447 sumAngle += curAngle;
1448
1449 }
1450
1451 }
1452 }
1453
1454 #endregion
1455
1456 #region 輔助方法
1457
1458 /// <summary>
1459 /// 計算X軸刻度值最大高度
1460 /// </summary>
1461 /// <returns></returns>
1462 private void Calc_XScaleHeight()
1463 {
1464 SizeF tmMaxSizeF = new SizeF(0, 0);
1465 for (int t = 0; t < XScaleValues.Count; t++)
1466 {
1467 SizeF tmSizeF = g.MeasureString(XScaleValues[t], new Font("宋體", 9));
1468 if (tmSizeF.Width > tmMaxSizeF.Width)
1469 {
1470 tmMaxSizeF.Width = tmSizeF.Width;
1471 tmMaxSizeF.Height = tmSizeF.Height;
1472 }
1473 }
1474 myXScaleMaxHeight = (((float)Math.Sqrt(tmMaxSizeF.Height * tmMaxSizeF.Height + tmMaxSizeF.Width * tmMaxSizeF.Width) - tmMaxSizeF.Height) * XRotateAngle / 90 + tmMaxSizeF.Height + 13f);
1475 }
1476
1477 /// <summary>
1478 /// 計算坐標Y軸的刻度值
1479 /// 適用於:曲線圖、柱狀圖
1480 /// 不適用:餅狀圖無需計算
1481 /// </summary>
1482 private void Calc_YScaleValue()
1483 {
1484 myMaxValue = 0; // 原始最大值
1485 myMinValue = 0; // 原始最小值
1486
1487 // 計算所有數據的最大值和最小值
1488 for (int mm = 0; mm < this.ChartDataArray.Count; mm++)
1489 {
1490 for (int nn = 0; nn < this.ChartDataArray[mm].Values.Count; nn++)
1491 {
1492 double iData = this.ChartDataArray[mm].Values[nn];
1493 if (mm == 0 && nn == 0)
1494 {
1495 myMaxValue = iData;
1496 myMinValue = iData;
1497 }
1498 else
1499 {
1500 myMaxValue = iData > myMaxValue ? iData : myMaxValue;
1501 myMinValue = iData > myMinValue ? myMinValue : iData;
1502 }
1503 }
1504 }
1505
1506 // 計算Y軸刻度
1507 double tmMax_New = Math.Ceiling(myMaxValue); // 目標最大值 向上取整
1508 double tmMin_New = Math.Floor(myMinValue); // 目標最小值 向下取整
1509 if (myMinValue == 0)
1510 {
1511 YScale_SplitValue = Math.Ceiling(tmMax_New / (double)YSplitNum); // 計算Y軸刻度間隔值
1512 YScale_StartValue = 0; // 計算Y軸刻度開始值
1513 }
1514 else
1515 {
1516 // 計算間隔值
1517 double tmJD1 = Math.Ceiling((tmMax_New - tmMin_New) / (double)(YSplitNum));
1518 double tmJD2 = tmJD1 - Math.Ceiling((tmMax_New - tmMin_New) / (double)(YSplitNum + 1));
1519 if (tmJD1 == 0) tmJD1 = 1;
1520 YScale_StartValue = tmJD1 * Math.Floor(tmMin_New / tmJD1); // 計算Y軸刻度開始值
1521 bool tmJDFlag = true;
1522 while (tmJDFlag)
1523 {
1524 if (YScale_StartValue <= tmMin_New && (YScale_StartValue + tmJD1 * YSplitNum) >= tmMax_New)
1525 {
1526 tmJDFlag = false;
1527 YScale_SplitValue = tmJD1; // 計算Y軸刻度間隔值
1528 break;
1529 }
1530 else
1531 {
1532 tmJD1 += tmJD2;
1533 }
1534 }
1535 }
1536
1537 // 計算Y軸刻度字符串的最大寬度
1538 SizeF tmYSizeF = g.MeasureString((YScale_StartValue + YScale_SplitValue * YSplitNum).ToString(), new Font("宋體", 9));
1539 myYScaleMaxWidth = tmYSizeF.Width + 9f;// 預留4個像素
1540 }
1541
1542 #endregion
1543
1544 #region 枚舉方法
1545
1546 /// <summary>
1547 /// 枚舉圖表類型
1548 /// </summary>
1549 public enum EChartType
1550 {
1551 /// <summary>
1552 /// 曲線圖
1553 /// </summary>
1554 Curve,
1555
1556 /// <summary>
1557 /// 柱狀圖
1558 /// </summary>
1559 Bar,
1560
1561 /// <summary>
1562 /// 餅狀圖
1563 /// </summary>
1564 Pie
1565
1566 }
1567
1568 /// <summary>
1569 /// 枚舉對齊方式
1570 /// </summary>
1571 public enum EAlign
1572 {
1573
1574 /// <summary>
1575 /// 居左
1576 /// </summary>
1577 left,
1578
1579 /// <summary>
1580 /// 居中
1581 /// </summary>
1582 center,
1583
1584 /// <summary>
1585 /// 居右
1586 /// </summary>
1587 right
1588 }
1589
1590 #endregion
1591
1592 }
1593
1594 /// <summary>
1595 /// 圖表數據單個項目
1596 /// </summary>
1597 public class EChartData
1598 {
1599 private string _name;
1600 private List<double> _values;
1601 private Color _itemColor;
1602 private SizeF _nameSize;
1603
1604 public EChartData()
1605 {
1606 _name = "";
1607 _values = new List<double>();
1608 _itemColor = Color.White;
1609 _nameSize = new SizeF(0, 0);
1610 }
1611
1612 /// <summary>
1613 /// 項目名稱
1614 /// </summary>
1615 public string Name
1616 {
1617 get { return _name; }
1618 set { _name = value; }
1619 }
1620
1621 /// <summary>
1622 /// 項目值數組
1623 /// </summary>
1624 public List<double> Values
1625 {
1626 get { return _values; }
1627 set { _values = value; }
1628 }
1629
1630 /// <summary>
1631 /// 文字大小
1632 /// 用戶繪制圖例時候使用
1633 /// </summary>
1634 public SizeF NameSize
1635 {
1636 get { return _nameSize; }
1637 set { _nameSize = value; }
1638 }
1639
1640 /// <summary>
1641 /// 項目顏色
1642 /// </summary>
1643 public Color ItemColor
1644 {
1645 get { return _itemColor; }
1646 set { _itemColor = value; }
1647 }
1648
1649 }
1650 }
View Code
表報設計DLL控件的源碼實在太多,這裡就不再一一貼出來了,下載完整的源碼自己調試運行查看。
此報表設計器結合上次的WEB打印控件,就組成了完整的報表設計。
報表設計器實例完整源碼下載地址:www.sdpsoft.com/==》下載中心==》報表設計器簡易源碼----自定義報表控件(源碼)以及在Winform中的使用源碼
或直接下載地址:winform報表設計工具源碼
歡迎廣大朋友一起交流。