记得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 }
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 }
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 }
表报设计DLL控件的源码实在太多,这里就不再一一贴出来了,下载完整的源码自己调试运行查看。
此报表设计器结合上次的WEB打印控件,就组成了完整的报表设计。
报表设计器实例完整源码下载地址:www.sdpsoft.com/==》下载中心==》报表设计器简易源码----自定义报表控件(源码)以及在Winform中的使用源码
或直接下载地址:winform报表设计工具源码
欢迎广大朋友一起交流。