报表特别多的业务逻辑,还是做成一个电子政务、文件管理系统比较合适。比如煤矿的储量管理,就针对报表!和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 }