程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> ExcelReport第二篇:ExcelReport源碼解析,excelreport第二篇

ExcelReport第二篇:ExcelReport源碼解析,excelreport第二篇

編輯:C#入門知識

ExcelReport第二篇:ExcelReport源碼解析,excelreport第二篇


導航

目   錄:基於NPOI的報表引擎——ExcelReport

上一篇:使用ExcelReport導出Excel

下一篇:擴展元素格式化器

概述

針對上一篇隨筆收到的反饋,在展開對ExcelReport源碼解析之前,我認為把編寫該組件時的想法分享給大家是有必要的。

編寫該組件時,思考如下:

1)要實現樣式、格式與數據的徹底分離。

為什麼要將樣式、格式與數據分離呢?恩,你不妨想一想在生成報表時,那些是變的而那些又是不變的。我的結論是:變的是數據。

有了這個想法,很自然的想到用模板去承載不變的部分(常量內容的樣式、格式、數據及變量內容的樣式、格式),在程序中控制變的部分(變量內容的數據)。

這裡以上一篇中的例子標識:

變量內容已使用粉紅色邊框標出,其余為常量內容。好了,相信“內容的數據”大家都知道那個是那個的。下面截圖,內容的樣式和格式。

現在我們回到上篇中使用的模板,相信你應該知道它承載那些東西了。

啰嗦了這麼多,總結一下樣式、格式與數據分離的好處:它讓我們編寫程序時關注更少(只需關心“變量內容的數據”)。

2)關注“變量內容的數據”填充到模板的最小單元(我們把這些稱之為元素格式化器),然後利用合成模式(Composite)搞定整個文檔的數據填充。

為什麼要抽象一個“元素格式化器”的概念呢?我們看數據源,我們有可能要將某個類型的數據填充到某個單元格、也可能將一個集合填充到多行、有可能將一張圖片填充到某個位置、也有可能就將某個字符串合並到某個單元格的內容中......如此種種。那麼它們有什麼共同點呢?它們都是填充“變量內容的數據”到模板的。

3)外部調用,統一從一個處理入口處理。

 

源碼解析

有了上面的背景,這張UML想必不難理解了。

當然,如果你還是覺得復雜, 沒關系。我會先介紹一下這裡面幾個重點關系。

1)從Export類開始吧!

這是一個靜態類,非常簡單。只有兩個靜態方法:ExportToLocal()、ExportToWeb()分別將生成的文件導出到本地和Web。這多半是廢話了,下面是重點:

這便引出了SheetFormatterContainer類,SheetFormatterContainer類是何許也?

  • SheetFormatterContainer是一個存儲“格式化一個Sheet用到的元素格式化器集合”的容器。

說到這,順便說下:ElementFormatter類和SheetFormatterContext類。

  • ElementFormatter:元素格式化器。
  • SheetFormatterContext:Sheet格式化上下文。

回到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下載最新源碼)。

2)附--(ExcelReport1.0源碼)

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.cs
/*
 類: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.cs
/*
 類: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.cs
/*
 類: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.cs
/*
 類: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 導出到Web
 
        public 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);
            }
        }
    }
}

 

源碼下載:

下載地址:https://github.com/hanzhaoxin/ExcelReport

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved