下一篇:扩展元素格式化器
针对上一篇随笔收到的反馈,在展开对ExcelReport源码解析之前,我认为把编写该组件时的想法分享给大家是有必要的。
编写该组件时,思考如下:
为什么要将样式、格式与数据分离呢?恩,你不妨想一想在生成报表时,那些是变的而那些又是不变的。我的结论是:变的是数据。
有了这个想法,很自然的想到用模板去承载不变的部分(常量内容的样式、格式、数据及变量内容的样式、格式),在程序中控制变的部分(变量内容的数据)。
这里以上一篇中的例子标识:
变量内容已使用粉红色边框标出,其余为常量内容。好了,相信“内容的数据”大家都知道那个是那个的。下面截图,内容的样式和格式。
现在我们回到上篇中使用的模板,相信你应该知道它承载那些东西了。
啰嗦了这么多,总结一下样式、格式与数据分离的好处:它让我们编写程序时关注更少(只需关心“变量内容的数据”)。
为什么要抽象一个“元素格式化器”的概念呢?我们看数据源,我们有可能要将某个类型的数据填充到某个单元格、也可能将一个集合填充到多行、有可能将一张图片填充到某个位置、也有可能就将某个字符串合并到某个单元格的内容中......如此种种。那么它们有什么共同点呢?它们都是填充“变量内容的数据”到模板的。
有了上面的背景,这张UML想必不难理解了。
当然,如果你还是觉得复杂, 没关系。我会先介绍一下这里面几个重点关系。
这是一个静态类,非常简单。只有两个静态方法:ExportToLocal()、ExportToWeb()分别将生成的文件导出到本地和Web。这多半是废话了,下面是重点:
这便引出了SheetFormatterContainer类,SheetFormatterContainer类是何许也?
说到这,顺便说下:ElementFormatter类和SheetFormatterContext类。
回到Export:
public static byte[] ExportToBuffer(string templateFile, params SheetFormatterContainer[] containers)
{var workbook = LoadTemplateWorkbook(templateFile);
foreach (var container in containers)
{var sheet = workbook.GetSheet(container.SheetName);
var context = new SheetFormatterContext(sheet, container.Formatters);context.Format();
}
return workbook.SaveToBuffer();}
如上代码,在执行导出的过程中,将每一个SheetFormatterContainer对象转换成了SheetFormatterContext对象。然后SheetFormatterContext对象调用自身的Format()方法格式化Sheet。
public void Format()
{if (null == Sheet || null == Formatters)
{ return;}
foreach (var formatter in Formatters)
{ formatter.Format(this);}
}
Formatters就是从SheetFormatterContainer传过来的“元素格式化器”集合。
抽象的“元素格式化器”:
至此,ExcelReport组件的核心部分已经介绍完成了,下面附上代码(如下代码仅供参考,了解ExcelReport组件最新动态,请到GitHub下载最新源码)。
SheetFormatterContainer.cs
/* 类:SheetFormatterContainer 描述:Sheet中元素的格式化器集合 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System.Collections.Generic;namespace ExcelReport{public class SheetFormatterContainer
{ #region 成员字段及属性private string sheetName;
public string SheetName
{ get { return sheetName; }}
private IEnumerable<ElementFormatter> formatters; public IEnumerable<ElementFormatter> Formatters { get { return formatters; }}
#endregion 成员字段及属性 #region 构造函数public SheetFormatterContainer(string sheetName, IEnumerable<ElementFormatter> formatters)
{ this.sheetName = sheetName; this.formatters = formatters;}
#endregion 构造函数}
}
/* 类:SheetFormatterContext 描述:Sheet格式化的上下文 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System.Collections.Generic;using NPOI.SS.UserModel;namespace ExcelReport{public class SheetFormatterContext
{ #region 成员字段及属性private int _increaseRowsCount = 0;
public ISheet Sheet { get; set; } public IEnumerable<ElementFormatter> Formatters { get; set; } #endregion 成员字段及属性 #region 构造函数 public SheetFormatterContext() {}
public SheetFormatterContext(ISheet sheet, IEnumerable<ElementFormatter> formatters) { this.Sheet = sheet; this.Formatters = formatters;}
#endregion 构造函数 #region 获取指定行当前行标 /// <summary> /// 获取指定行当前行标 /// </summary> /// <param name="rowIndexInTemplate">指定行在模板中的行标</param> /// <returns>当前行标</returns>public int GetCurrentRowIndex(int rowIndexInTemplate)
{ return rowIndexInTemplate + _increaseRowsCount;}
#endregion 获取指定行当前行标 #region 在指定行后插入一行(并将指定行作为模板复制样式) /// <summary> /// 在指定行后插入一行(并将指定行作为模板复制样式) /// </summary> /// <param name="templateRowIndex">模板行在模板中的行标</param>public void InsertEmptyRow(int templateRowIndex)
{var templateRow = Sheet.GetRow(GetCurrentRowIndex(templateRowIndex));
var insertRowIndex = GetCurrentRowIndex(templateRowIndex + 1);
if (insertRowIndex < Sheet.LastRowNum) {Sheet.ShiftRows(insertRowIndex, Sheet.LastRowNum, 1, true, false);
}
var newRow = Sheet.CreateRow(GetCurrentRowIndex(templateRowIndex + 1));
_increaseRowsCount++;
foreach (var cell in templateRow.Cells)
{newRow.CreateCell(cell.ColumnIndex).CellStyle = cell.CellStyle;
}
}
#endregion 在指定行后插入一行(并将指定行作为模板复制样式) #region 清除指定行单元格内容 /// <summary> /// 清除指定行单元格内容 /// </summary> /// <param name="rowIndex">指定行在模板中的行标</param>public void ClearRowContent(int rowIndex)
{var row = Sheet.GetRow(GetCurrentRowIndex(rowIndex));
foreach (var cell in row.Cells)
{ cell.SetCellValue(string.Empty);}
}
#endregion 清除指定行单元格内容 #region 删除指定行 /// <summary> /// 删除指定行 /// </summary> /// <param name="rowIndex">指定行在模板中的行标</param>public void RemoveRow(int rowIndex)
{var row = Sheet.GetRow(GetCurrentRowIndex(rowIndex));
Sheet.RemoveRow(row);
}
#endregion 删除指定行 #region 格式化Sheet /// <summary> /// 格式化Sheet /// </summary>public void Format()
{if (null == Sheet || null == Formatters)
{ return;}
foreach (var formatter in Formatters)
{ formatter.Format(this);}
}
#endregion 格式化Sheet}
}
/* 类:ElementFormatter 描述:(元素)格式化器(抽象) 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System;using NPOI.SS.UserModel;namespace ExcelReport{public abstract class ElementFormatter
{ #region 设置单元格值protected virtual void SetCellValue(ICell cell, object value)
{if (null == cell)
{ return;}
if (null == value)
{ cell.SetCellValue(string.Empty);}
else { var valueTypeCode = Type.GetTypeCode(value.GetType()); switch (valueTypeCode) {case TypeCode.String: //字符串类型
cell.SetCellValue(Convert.ToString(value)); break;case TypeCode.DateTime: //日期类型
cell.SetCellValue(Convert.ToDateTime(value)); break;case TypeCode.Boolean: //布尔型
cell.SetCellValue(Convert.ToBoolean(value)); break;case TypeCode.Int16: //整型
case TypeCode.Int32: case TypeCode.Int64: case TypeCode.Byte:case TypeCode.Single: //浮点型
case TypeCode.Double:case TypeCode.UInt16: //无符号整型
case TypeCode.UInt32: case TypeCode.UInt64: cell.SetCellValue(Convert.ToDouble(value)); break; default: cell.SetCellValue(string.Empty); break;}
}
}
#endregion 设置单元格值 #region 格式化操作public abstract void Format(SheetFormatterContext context);
#endregion 格式化操作}
}
/* 类:CellFormatter 描述:单元格(元素)格式化器 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System.Drawing;namespace ExcelReport{public class CellFormatter : ElementFormatter
{ #region 成员字段及属性 private Point _cellPoint;private object _value;
#endregion 成员字段及属性 #region 构造函数public CellFormatter(Point cellPoint, object value)
{_cellPoint = cellPoint;
_value = value;}
public CellFormatter(int rowIndex, int columnIndex, object value)
{ _cellPoint = new Point(rowIndex, columnIndex); _value = value;}
#endregion 构造函数 #region 格式化操作public override void Format(SheetFormatterContext context)
{var rowIndex = context.GetCurrentRowIndex(_cellPoint.X);
var row = context.Sheet.GetRow(rowIndex);
if (null == row)
{row = context.Sheet.CreateRow(rowIndex);
}
var cell = row.GetCell(_cellPoint.Y);
if (null == cell)
{cell = row.CreateCell(_cellPoint.Y);
}
SetCellValue(cell, _value);
}
#endregion 格式化操作}
}
/* 类:TableFormatter 描述:表格(元素)格式化器 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System;using System.Collections.Generic;namespace ExcelReport{public class TableFormatter<TSource> : ElementFormatter
{ #region 成员字段private int _templateRowIndex;
private IEnumerable<TSource> _dataSource; private List<TableColumnInfo<TSource>> _columnInfoList; #endregion 成员字段 #region 构造函数public TableFormatter(int templateRowIndex, IEnumerable<TSource> dataSource, params TableColumnInfo<TSource>[] columnInfos)
{_templateRowIndex = templateRowIndex;
_dataSource = dataSource;
_columnInfoList = new List<TableColumnInfo<TSource>>();if (null != columnInfos && columnInfos.Length > 0)
{_columnInfoList.AddRange(columnInfos);
}
}
#endregion 构造函数 #region 格式化操作public override void Format(SheetFormatterContext context)
{ context.ClearRowContent(_templateRowIndex); //清除模板行单元格内容if (null == _columnInfoList || _columnInfoList.Count <= 0 || null == _dataSource)
{ return;}
foreach (TSource rowSource in _dataSource)
{var row = context.Sheet.GetRow(context.GetCurrentRowIndex(_templateRowIndex));
foreach (TableColumnInfo<TSource> colInfo in _columnInfoList)
{var cell = row.GetCell(colInfo.ColumnIndex);
SetCellValue(cell, colInfo.DgSetValue(rowSource));
}
context.InsertEmptyRow(_templateRowIndex); //追加空行}
context.RemoveRow(_templateRowIndex); //删除空行}
#endregion 格式化操作 #region 添加列信息public void AddColumnInfo(TableColumnInfo<TSource> columnInfo)
{_columnInfoList.Add(columnInfo);
}
public void AddColumnInfo(int columnIndex, Func<TSource, object> dgSetValue)
{ _columnInfoList.Add(new TableColumnInfo<TSource>(columnIndex, dgSetValue));}
#endregion 添加列信息}
}
ExportHelper.cs
/* 类:ExportHelper 描述:导出助手类 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System.IO;using NPOI.SS.UserModel;namespace ExcelReport{internal static class ExportHelper
{ #region 加载模板,获取IWorkbook对象private static IWorkbook LoadTemplateWorkbook(string templateFile)
{using (var fileStream = new FileStream(templateFile, FileMode.Open, FileAccess.Read)) //读入excel模板
{ return WorkbookFactory.Create(fileStream);}
}
#endregion 加载模板,获取IWorkbook对象 #region 将IWorkBook对象转换成二进制文件流private static byte[] SaveToBuffer(this IWorkbook workbook)
{using (var ms = new MemoryStream())
{workbook.Write(ms);
ms.Flush();
ms.Position = 0;
return ms.GetBuffer();}
}
#endregion 将IWorkBook对象转换成二进制文件流 #region 导出格式化处理后的文件到二进制文件流public static byte[] ExportToBuffer(string templateFile, params SheetFormatterContainer[] containers)
{var workbook = LoadTemplateWorkbook(templateFile);
foreach (var container in containers)
{var sheet = workbook.GetSheet(container.SheetName);
var context = new SheetFormatterContext(sheet, container.Formatters);context.Format();
}
return workbook.SaveToBuffer();}
#endregion 导出格式化处理后的文件到二进制文件流}
}
Export.cs
/* 类:Export 描述:导出 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System;using System.IO;using System.Web;namespace ExcelReport{public static class Export
{ #region 导出到本地public static void ExportToLocal(string templateFile, string targetFile, params SheetFormatterContainer[] containers)
{ #region 参数验证if (string.IsNullOrWhiteSpace(templateFile))
{throw new ArgumentNullException("templateFile");
}
if (string.IsNullOrWhiteSpace(targetFile))
{throw new ArgumentNullException("targetFile");
}
if (!File.Exists(templateFile)) {throw new FileNotFoundException(templateFile + " 文件不存在!");
}
#endregion 参数验证 using (FileStream fs = File.OpenWrite(targetFile)) {var buffer = ExportHelper.ExportToBuffer(templateFile, containers);
fs.Write(buffer, 0, buffer.Length);
fs.Flush();
}
}
#endregion 导出到本地 #region 导出到Webpublic static void ExportToWeb(string templateFile, string targetFile, params SheetFormatterContainer[] containers)
{ #region 参数验证if (string.IsNullOrWhiteSpace(templateFile))
{throw new ArgumentNullException("templateFile");
}
if (string.IsNullOrWhiteSpace(targetFile))
{throw new ArgumentNullException("targetFile");
}
if (!File.Exists(templateFile)) {throw new FileNotFoundException(templateFile + " 文件不存在!");
}
#endregion 参数验证 HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(targetFile, System.Text.Encoding.UTF8));
HttpContext.Current.Response.BinaryWrite(ExportHelper.ExportToBuffer(templateFile, containers));
HttpContext.Current.Response.End();
}
#endregion 导出到Web}
}
Parameter.cs
/* 类:Parameter 描述:参数信息 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System.Drawing;namespace ExcelReport{public class Parameter
{ public Parameter() {}
public Parameter(string sheetName, string parameterName, Point cellPoint)
{ this.SheetName = sheetName; this.ParameterName = parameterName; this.CellPoint = cellPoint;}
public string SheetName { set; get; }
public string ParameterName { set; get; }
public Point CellPoint { set; get; }}
}
ParameterCollection.cs
/* 类:ParameterCollection 描述:模板中参数信息的集合 编 码 人:韩兆新 日期:2015年01月17日 修改记录:*/using System.Collections.Generic;using System.Drawing;using System.IO;using System.Xml.Serialization;namespace ExcelReport{public class ParameterCollection
{protected List<Parameter> parameterList = new List<Parameter>();
public Point this[string sheetName, string parameterName]
{get
{foreach (Parameter parameter in parameterList)
{ if (parameter.SheetName.Equals(sheetName) && parameter.ParameterName.Equals(parameterName)) { return parameter.CellPoint;}
}
return new Point();
}
set
{bool isExist = false;
foreach (Parameter parameter in parameterList)
{ if (parameter.SheetName.Equals(sheetName) && parameter.ParameterName.Equals(parameterName)) { isExist = true; parameter.CellPoint = value;}
}
if (!isExist) {parameterList.Add(new Parameter(sheetName, parameterName, value));
}
}
}
public void Load(string xmlPath)
{ string fileName = xmlPath; if (File.Exists(fileName)) { XmlSerializer xmlSerializer = new XmlSerializer(parameterList.GetType());using (Stream reader = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{ parameterList = xmlSerializer.Deserialize(reader) as List<Parameter>;}
}
else { parameterList = new List<Parameter>();}
}
public void Save(string xmlPath)
{ string fileName = xmlPath; FileInfo fileInfo = new FileInfo(fileName);DirectoryInfo directoryInfo = fileInfo.Directory;
if (!directoryInfo.Exists) {directoryInfo.Create();
}
XmlSerializer xmlSerializer = new XmlSerializer(parameterList.GetType());using (Stream writer = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{xmlSerializer.Serialize(writer, parameterList);
}
}
}
}
ExcelReport第二篇:ExcelReport源码解析
原文:http://www.cnblogs.com/hanzhaoxin/p/4240398.html