报表特别多的业务逻辑,还是做成一个电子政务、文件管理系统比较合适。比如煤矿的储量管理,就针对报表!和OA系统很类似啊!有些侧重信息管理如地质信息,水文信息,当然也有报表,但是相对来说就少的多了!还是做成信息管理系统,用数据库比较好!对于煤矿机电设备设施管理不好说,感觉各有利弊!
今天重新拾起用Excel输出报表的工作。别人封装的来实在是看不懂!
基本思路:任何一个报表都可以划分成若干区域,如图的课程表用红色框划分成了7个区域。
(似乎用树来组织会不错,如果能有个配置文件,能否不用编码就能实现任何报表的生成?)
水晶报表的思想是报表包含子报表。如果类比这种思想报表->子报表->区域,那么区域就是输出报表的最小单位。区域包含一系列相同的特性:边框、对齐方式、颜色、字体。区域的确定通过左上角点Cell和行列数目。运用Excel自带的互操作集,划分的区域可以再次划分,如7区域可以再进一步划分,Range可以支持反复的写入。这里只是初步实现了基本功能,区域包含的属性包括:起始xlsCell、行数、列数、object[,]数据、对齐方式、边框(是一个16进制的整数值)、字体、颜色、单元宽度、单元格行高等属性。
目前的类关系是区域(父类)<--标题类,如果使用多工厂方法模式或者简单工厂模式,似乎可以,把区域作为抽象,不过似乎有没有那个必要。再考虑吧。
区域定义如下:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using Excel=Microsoft.Office.Interop.Excel;
6 using System.Reflection;
7 namespace NtExcel
8 {
9
10 public class xlsRegion
11 {
12 public const uint Grid_InsideHorizontal = 0x0001;
13 public const uint Grid_InsideVertical = 0x0002;
14 public const uint Grid_EdgeTop = 0x0004;
15 public const uint Grid_EdgeBottom = 0x0008;
16 public const uint Grid_EdgeLeft = 0x0010;
17 public const uint Grid_EdgeRight = 0x0020;
18
19
20 public static Excel._Application xlApp;
21 public static Excel.Worksheet sheet;
22
23 protected Excel.Range range;
24 //起点单元格
25 protected xlsCell startCell;
26 //行数
27 int nRowsCount = 0;
28 public int RowsCount
29 {
30 get{return nRowsCount;}
31 set{ nRowsCount=value;}
32 }
33 //列数
34 int nColumnsCount = 0;
35 public int ColumnsCount
36 {
37 get
38 {
39 return nColumnsCount;
40 }
41 set { nColumnsCount = value; }
42 }
43 //合并单元格
44 protected bool IsMergeCells;
45 public bool MergeCells
46 {
47 get { return IsMergeCells; }
48 set { IsMergeCells = value;}
49 }
50 //对齐方式
51 Excel.Constants align;
52
53 public Excel.Constants Alignment
54 {
55 get { return align; }
56 set { align = value; }
57 }
58 //边框
59 uint gridLine;
60
61 public uint GridLine
62 {
63 get { return gridLine; }
64 set { gridLine = value; }
65 }
66 //数据
67 object[,] objdata;
68
69 public object[,] Objdata
70 {
71 get { return objdata; }
72 set { objdata = value; }
73 }
74 //构造函数
75 public xlsRegion(xlsCell _startCell, int _RowCount, int _ColumnCount)
76 {
77 startCell = _startCell;
78 int _endRowIndex = _startCell.RowIndex + _RowCount - 1;
79 int _endColumnIndex = _startCell.ColumnIndex + _ColumnCount - 1;
80 xlsCell endCell = new xlsCell(_endRowIndex, _endColumnIndex);//依赖值
81 range = sheet.get_Range(xlApp.Cells[_startCell.RowIndex, startCell.ColumnIndex], xlApp.Cells[endCell.RowIndex, endCell.ColumnIndex]);
82 }
83 public xlsRegion(string strRange)
84 {
85 //正则表达式验证
86 range = sheet.get_Range(strRange, Missing.Value);
87 }
88 public virtual void SetStyle()
89 {
90 //range.HorizontalAlignment = align;
91 range.MergeCells = IsMergeCells;
92 DrawGridLine();
93 }
94 protected void DrawGridLine()
95 {
96 if ((gridLine&Grid_InsideHorizontal)==Grid_InsideHorizontal)
97 { range.Borders.get_Item(Excel.XlBordersIndex.xlInsideHorizontal).Weight = Excel.XlBorderWeight.xlThin;
98 }
99 if ((gridLine&Grid_InsideVertical)==Grid_InsideVertical)
100 {range.Borders.get_Item(Excel.XlBordersIndex.xlInsideVertical).Weight = Excel.XlBorderWeight.xlThin;
101 }
102 if ((gridLine&Grid_EdgeTop)==Grid_EdgeTop)
103 {range.Borders.get_Item(Excel.XlBordersIndex.xlEdgeTop).Weight = Excel.XlBorderWeight.xlThin;
104 }
105 if ((gridLine&Grid_EdgeBottom)==Grid_EdgeBottom)
106 {range.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).Weight = Excel.XlBorderWeight.xlThin;
107 }
108 if ((Grid_EdgeLeft&gridLine)==Grid_EdgeLeft)
109 {range.Borders.get_Item(Excel.XlBordersIndex.xlEdgeLeft).Weight = Excel.XlBorderWeight.xlThin;
110 }
111 if ((Grid_EdgeRight&gridLine)==Grid_EdgeRight)
112 {range.Borders.get_Item(Excel.XlBordersIndex.xlEdgeRight).Weight = Excel.XlBorderWeight.xlThin;
113 }
114
115 }
116 public virtual void FillData()
117 {
118 range.Value2 = objdata;
119 }
120
121 }
122
123 }

1 public class xlsCell 2 { 3 int nRowIndex; 4 5 public int RowIndex 6 { 7 get { return nRowIndex; } 8 set { nRowIndex = value; } 9 } 10 int nColumnIndex; 11 12 public int ColumnIndex 13 { 14 get { return nColumnIndex; } 15 set { nColumnIndex = value; } 16 } 17 public xlsCell(int _RowIndex, int _ColumnIndex) 18 { 19 nRowIndex = _RowIndex; 20 nColumnIndex = _ColumnIndex; 21 } 22 public void MoveTo(int _RowIndex, int _ColumnIndex) 23 { 24 nRowIndex = _RowIndex; 25 nColumnIndex = _ColumnIndex; 26 } 27 }
为方便起见:从xlsRegion继承了标题类和列头类,图上1和3区域。

1 public class xlsTitle: xlsRegion
2 {
3 public xlsTitle(xlsCell _startCell, int _cols)
4 : base(_startCell, 1, _cols)
5 {
6 base.IsMergeCells = true;
7 base.Alignment = Excel.Constants.xlCenter;
8 base.GridLine = Grid_EdgeBottom | Grid_EdgeTop|Grid_EdgeLeft|Grid_EdgeRight;
9 }
10 string title;
11
12 public string Title
13 {
14 get { return title; }
15 set { title = value; }
16 }
17 public override void FillData()
18 {
19 range.Value2=title;
20 }
21 public override void SetStyle()
22 {
23 range.MergeCells = base.IsMergeCells;
24 range.HorizontalAlignment = base.Alignment;
25 base.DrawGridLine();
26 }
27 }

1 public class xlsColumnHeader:xlsRegion
2 {
3 public xlsColumnHeader(xlsCell _startCell,int _ColumnsCount )
4 : base(_startCell, 1, _ColumnsCount)
5 {
6 base.Alignment=Excel.Constants.xlCenter;
7 base.GridLine=Grid_EdgeBottom|Grid_EdgeTop|Grid_EdgeLeft|Grid_EdgeRight;
8 }
9 List<string> headers;
10
11 public List<string> Headers
12 {
13 get { return headers; }
14 set { headers = value; }
15 }
16 public override void FillData()
17 {
18 int i=0;
19 Excel.Range rg;
20 foreach (string str in headers)
21 {
22 rg = xlApp.get_Range(xlApp.Cells[startCell.RowIndex, startCell.ColumnIndex+i], xlApp.Cells[startCell.RowIndex, startCell.ColumnIndex + i]);
23 i++;
24 rg.Value2 = str;
25 }
26
27 }
28 public override void SetStyle()
29 {
30 range.MergeCells = base.IsMergeCells;
31 range.HorizontalAlignment = base.Alignment;
32 base.DrawGridLine();
33 }
Excel导出报表的流程一般为:启动Excel应用程序,初始化工作簿,初始化sheet,写入报表,保存文件,关闭工作簿,退出Excel。
因此封装工作流程类:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using Excel = Microsoft.Office.Interop.Excel; 6 using System.Diagnostics; 7 namespace NtExcel 8 { 9 public class xlsApp 10 { 11 public Excel._Application xlApp; 12 public Excel.Worksheet sheet; 13 Excel.Workbook book; 14 public void InitReport(string _Sheetname,int _SheetIndex=1) 15 { 16 xlApp = new Excel.ApplicationClass(); 17 if (xlApp == null) 18 { 19 throw new ArgumentException("Excel无法启动,可能是您未安装Excel软件."); 20 } 21 object missing = System.Reflection.Missing.Value; 22 xlApp.Workbooks.Add(missing); 23 24 book = xlApp.Workbooks[1]; 25 sheet = (Excel.Worksheet)book.Sheets[_SheetIndex]; 26 sheet.Name = _Sheetname; 27 } 28 public void KillExcelProcess() 29 { 30 Process[] myProcesses; 31 myProcesses = Process.GetProcessesByName("Excel"); 32 33 foreach (Process myProcess in myProcesses) 34 { 35 myProcess.Kill(); 36 } 37 } 38 private void ReleaseObject(object obj) 39 { 40 try 41 { 42 System.Runtime.InteropServices.Marshal.ReleaseComObject(obj); 43 } 44 catch { } 45 finally { obj = null; } 46 } 47 public void SaveReport(string filename) 48 { 49 xlApp.DisplayAlerts = false; 50 book.SaveCopyAs(filename); 51 xlApp.Workbooks.Close(); 52 xlApp.Quit(); 53 sheet = null; 54 this.ReleaseObject(book); 55 this.ReleaseObject(xlApp); 56 } 57 } 58 }
位运算的应用:
借鉴Window窗体样式设置的思想用‘|’按位并操作实现样式的叠加。因为区域的边框有六种样式,用户可以设置不同的样式组合。每一种特性用二进制的0和1表示,每位表示一种特性。如A=0001、B=0010、C=0100、D=1000表示四种特性,通用'|'运算获得style,style1=A|B|C=0111,style2=B|C=0110。用'&'获的是否包含此特性,style&A=0001,style2&A=0000。
public const uint Grid_InsideHorizontal = 0x0001;
public const uint Grid_InsideVertical = 0x0002;
public const uint Grid_EdgeTop = 0x0004;
public const uint Grid_EdgeBottom = 0x0008;
public const uint Grid_EdgeLeft = 0x0010;
public const uint Grid_EdgeRight = 0x0020;
类的调用实例如下:

1 private void button2_Click(object sender, EventArgs e) 2 { 3 OracleHelper.connectionString = "Data Source =demo; User Id =peouser; Password =123; Integrated Security = no"; 4 csRegions reg = new csRegions(); 5 reg.Fetch(); 6 object[,] objs = new object[csRegions.list.Count, 1]; 7 int i=0; 8 foreach (string str in csRegions.list) 9 { 10 objs[i, 0] = str; 11 i++; 12 } 13 string VirFileName = "D:\\" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xls"; 14 15 xlsApp excel = new xlsApp(); 16 excel.InitReport("很好啊"); 17 18 xlsRegion.xlApp = excel.xlApp; 19 xlsRegion.sheet = excel.sheet; 20 21 xlsCell cel1 = new xlsCell(1, 1); 22 xlsTitle xls_Title = new xlsTitle(cel1, 10); 23 xls_Title.Title = "我是中国人!"; 24 xls_Title.SetStyle(); 25 xls_Title.FillData(); 26 27 cel1.MoveTo(2, 1); 28 int rowsData = objs.GetLength(0); 29 xlsRegion xls_Region = new xlsRegion(cel1, rowsData, 1); 30 xls_Region.Objdata = objs; 31 xls_Region.FillData(); 32 xls_Region.SetStyle(); 33 34 cel1.MoveTo(2, 2); 35 xlsColumnHeader xls_Column = new xlsColumnHeader(cel1,9); 36 List<string> listheader = new List<string>(); 37 listheader.Add("姓名"); 38 listheader.Add("工种"); 39 xls_Column.Headers = listheader; 40 xls_Column.FillData(); 41 xls_Column.SetStyle(); 42 43 excel.SaveReport(VirFileName); 44 GC.Collect(); 45 46 excel.KillExcelProcess(); 47 }