Excel文档处理之Open XML

引言

在使用Open XML处理Excel文档之前,需要先确定它的作用范围,Open XML适用于2007版本及以上的Excel文档的处理。

第二步就是配置Open XML的开发环境:Open XML相关下载链接。

1、新建Excel文档

咱们先从一个实际的Excel文档入手,在桌面上新建一个Excel文档,默认会有一个Sheet,可以理解为一个工作表或一个页面。

Excel文档处理之Open XML_第1张图片   

创建一个Excel所等价的代码如下:

//本代码段为简化代码,只作理解之用。
//SpreadsheetDocument:Excel文档的变量类型
//以path中所指定的地址和名称创建一个Excel文档
//SpreadsheetDocumentType.Workbook:指明创建的文件类型为后缀名为.xlsx的Excel文档
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook);
将刚刚新建的Excel文档的后缀名改为rar(压缩包格式),然后打开压缩包,看到如下图所示的目录,其中xl文件夹中的文件很重要,需要仔细研究。打开xl文件夹。

Excel文档处理之Open XML_第2张图片Excel文档处理之Open XML_第3张图片Excel文档处理之Open XML_第4张图片

先打开workbook.xml,默认会用浏览器显示其内容,workbook.xml主要对sheet(理解为工作表或页面)进行宏观性全局性的描述。下图中,第二行开始到结束,由叫做“workbook”的元素包裹在最外层,可以把workbook理解为工作薄,但不完全等价于Excel文件。红色方框框起来的元素为“ sheets”,它的内部描述了该文档有几个sheet(理解为工作表或页面)。由于新建的Excel文档默认只有一个sheet,所以“sheets”元素的内部只有一个“sheet”元素,对应着创建Excel文档后默认的“Sheet1”工作表,图中的sheet元素内部,有三个必备属性:r:id、sheetId、name。r:id是字符串类型的属性,以“rId”+数字作为其内容;sheetId为数字类型的属性;name就是该sheet的名字。前两个属性都属于sheet的id值,都是隐含的内在的属性,name属性是外在的显露出来的属性,图中name属性的值“Sheet1”刚好对应新建的Excel中的默认的sheet名称。

Excel文档处理之Open XML_第5张图片

2、添加一个sheet工作表

倘若在Excel中添加一个工作表,并将其命名为“测试”,会有什么变化呢?具体的操作要么把Excel文件后缀名恢复原样,要么可以再新建一个Excel,并添加一个工作表。再次打开workbook.xml文件时,会发现多了一个sheet元素。该sheet元素对应的r:id和sheetId的值都比默认的sheet元素的值增加了1,name属性的值为“测试”。

Excel文档处理之Open XML_第6张图片

添加一个工作表等价的代码如下:

//本代码段为简化代码,只作理解之用。
//workbookPart:管理Workbook工作薄
//WorksheetPart:管理工作表,一个WorksheetPart对应一个工作表
//workbookPart.AddNewPart():添加一个新的WorksheetPart
WorksheetPart worksheetPart = workbookPart.AddNewPart();
//workbookPart.Workbook.Sheets:Workbook元素中的Sheets元素
//Sheets.Elements():Sheets元素中Sheet元素的集合
//Elements().LastOrDefault():Sheet元素集合中最后一个Sheet元素或默认的Sheet元素
Sheet sheet = workbookPart.Workbook.Sheets.Elements().LastOrDefault();
//定义默认的sheetId值
//当工作薄中一个工作表也没有时,便以1作为新添加的工作表的Sheet元素的Id值
//当工作薄中存在工作表时,便将最后一个工作表的Id值加1作为新添加的工作表的Sheet元素的Id值UInt32Value sheetId = 1;
if (sheet != null)
{
    sheetId = sheet.SheetId + 1;
}
//AppendChild:添加一个子元素
workbookPart.Workbook.Sheets.AppendChild(new Sheet()
{
    //Id为字符串类型的Id属性
    Id = workbookPart.GetIdOfPart(worksheetPart),
    //SheetId为数值类型的Id属性
    SheetId = sheetId,
    //工作表的名称
    Name = sheetName
});

3、向sheet工作表中添加内容

在上方压缩包目录图中,打开worksheets文件夹。该目录下有一个名为“sheet1.xml"的文件,对应默认的”Sheet1“工作表。有多少个工作表,这个文件夹里面就有多少个sheet文件,sheet文件对所有非空表格的信息都进行了登记。

打开sheet1.xml文件,内容如下图所示。其中sheetViews元素可以描述当前选中的表格所在的位置,由于新建的Excel文档没有选中表格,所有sheetViews没有描述;sheetData元素记录非空表格的位置及表格内容,同样,由于Excel没有写入任何内容,sheetData中也就没有记录。

Excel文档处理之Open XML_第7张图片

这时,对空白的Sheet1工作表添加如下图所示的内容。分别在A1、B2、C3单元格添加了“1”、“数据”、“Data”,并选中B1单元格。添加这些数据后,sheet1.xml文件主要的变化如下图。sheetViews元素中,selection元素描述选中的单元格为B1。sheetData元素中,有三个row元素,分别对应添加的“1”、“数据”、“Data”;在row元素中,r属性表示第几行,c元素记录非空表格的位置及表格内容;在c元素中,r元素表示单元格的位置,其中第一个c元素中,v元素表示单元格的内容,第二个和第三个c元素的解释请看下面的内容。

Excel文档处理之Open XML_第8张图片

在xl文件夹中,多了一个名为sharedStrings.xml的文件,这个文件名字面上的意思是共享字符串,这是Excel中很有意思的一个东西,它将可能会重复用到的字符串都存储于这个文件中。打开它,内容如下图所示,si元素中存储着可能会重复用到的字符串,可以想象,当N个单元格中的内容都是“数据”时,那么Excel就不用为每个单元格分配额外的存储空间,而只需要在上图中的v元素中存储该字符串的引用即可,例:在下图中,数据在sharedStrings.xml文件中的位置是第0个(默认从第0个开始),上图中c元素中t属性赋值为“s”,表明该单元格的内容为共享字符串,v元素中的值为0,表示该单元格中的内容为sharedStrings.xml文件中第0个字符串。

Excel文档处理之Open XML_第9张图片Excel文档处理之Open XML_第10张图片

向表格中添加内容的代码如下:

//本代码段为简化代码,只作理解之用。
//SheetData:记录非空单元格的位置及内容
//worksheetPart:管理工作表,一个WorksheetPart对应一个工作表
SheetData sheetData = worksheetPart.Worksheet.Elements().FirstOrDefault();
//Row:工作表中的行
Row row = sheetData.Elements().LastOrDefault();
//添加普通数据
void AddData(object data)
{
    Cell cell = row.AppendChild(new Cell()
    {
        //CellValue:单元格内容
        CellValue = new CellValue() { Text = data.ToString() },
        //DataType:单元格类型
        DataType = CellValues.String,
    });
}
//添加共享字符串
private void AddSharedString(object data)
{
    Cell cell = row.AppendChild(new Cell()
    {
        CellValue = new CellValue(Convert.ToString(GetSharedStringItemIndex(data.ToString()))),
        DataType = CellValues.SharedString,
    });
}
//获取指定字符串在SharedStringTable中的索引值,不存在就创建
int GetSharedStringItemIndex(string value)
{
    //字符串从0开始标记
    int index = 0;
    //寻找是否有与value相同的字符串,若有,则将index设置为对应的标记值,并返回
    //SharedStringItem:共享字符串的数据类型
    //sharedStringTable:共享字符串表
    foreach (SharedStringItem item in sharedStringTable.Elements())
    {
        if (item.InnerText == value)
        {
            return index;
        }
        index++;
    }
    //若没有与value相同的字符串,则添加一个字符串到共享字符串表中,并将其内容设置为value
    sharedStringTable.AppendChild(new SharedStringItem(new Text(value)));
    return index;
}

4、表格样式

在xl文件夹中有一个styles.xml文件,这个文件保存着单元格的格式,包含背景、边框、字体等设置。在刚刚新建的Excel文档中,styles.xml中主要内容如下列图所示。fonts: 以font元素为单位存储字体样式;fills:以fill元素为单位存储填充样式;borders:以border元素为单位存储边框样式。以上每个元素都有一个相同的属性count,用于表示该元素包含几个子元素。在font元素中,sz:字体大小;color:字体颜色;name:字体类型。在fill元素中,patternFill:填充样式。在border元素中,left、right、top、bottom分别表示左右上下边框的样式。

Excel文档处理之Open XML_第11张图片Excel文档处理之Open XML_第12张图片Excel文档处理之Open XML_第13张图片

cellXfs:以xf为单位存储表格样式,此处存储的表格样式是对font元素、fill元素、border元素的调用和组合。在xf元素中,将第0个border元素、第0个fill元素和第0个font元素组合成一个表格样式。在n个表格样式中,往往会有相同的字体样式、填充样式、边框样式,因此使用引用的方式设置表格样式可以节约存储空间。再者,在常规的Excel文档中,往往一大堆内容使用同一个表格样式,使用引用的方式调用表格样式可以节省大量存储空间。

倘若将第二行第二列的“数据”的单元格填充颜色设置为黄色,字体颜色设置为红色,worksheets文件夹中的sheet1.xml将会发生如下图所示的主要变化,在第二个row元素(即r 属性的值为2的row元素)中,位置为B2的c元素的s属性(单元格样式style)的值为1,表示该单元格的单元格样式引用上图中cellXfs元素中的第1个xf元素(上图中只有第0 个 xf元素)。

       

上方填充颜色和字体颜色的设置导致xl文件夹中styles.xml也发生了相应的变化,如下列三张图所示。多了一个font元素,该元素中,color属性的值“FFFF0000”代表红色,意为字体颜色为红色;多了一个fill元素,该元素中,fgColor的值“FFFFFF00”代表黄色,以为填充颜色为黄色;cellXfs元素中多了一个xf元素,fontId值为“2”,fontId值为“2”,即引用下方第一张图中的font和第二张图中的fill,并组合成一个单元格样式,供上图中红色方框内的s属性调用。

Excel文档处理之Open XML_第14张图片


上述添加单元格样式的代码如下:

//本代码段为简化代码,只作理解之用。
//Stylesheet:单元格样式
Stylesheet stylesheet = workbookPart.WorkbookStylesPart.Stylesheet;
//添加一个填充样式
Fill fill = stylesheet.Fills.AppendChild(new Fill()
{
    PatternFill = new PatternFill()
    {
        PatternType = PatternValues.Solid,
        ForegroundColor = new ForegroundColor()
        {
            Rgb = new HexBinaryValue((Color.Yellow).ToArgb().ToString("X"))
        }
    }
});
//添加一个字体样式
Font font = stylesheet.Fonts.AppendChild(new Font()
{
      Color = new Color()
      {
            Rgb=new HexBinaryValue((Color.Red).ToArgb().ToString("X"))
      }
});
//添加一个单元格样式
stylesheet.CellFormats.AppendChild(new CellFormat()
{
      //引用上方定义的fill
    FillId = (uint)fill.Index,
      //引用上方定义的font
      FontId = (uint)font.Index
});

5、代码

代码分为两个部分:OXExcel类:Excel文档结构层次的节点或元素的处理;OXSheet:工作表内容层次的节点或元素的处理。

注:本代码段是本人经多次试验后的结果,但依然不尽如人意,主要表现在两个方面:1、表格样式部分只编写了修改背景颜色的功能;2、超链接的添加会导致一个后遗症,打开Excel文档后不修改任何东西,关闭时总会有一个是否保存的提示。

using System;
using System.Linq;
using System.Runtime.InteropServices;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.IO;

namespace OpenXMLDemo
{
    public class OXExcel
    {
        //判断文件是否只读的相关定义
        [DllImport("kernel32.dll")]
        public static extern IntPtr _lopen(string lpPathName, int iReadWrite);
        [DllImport("kernel32.dll")]
        public static extern bool CloseHandle(IntPtr hObject);
        public const int OF_READWRITE = 2;
        public const int OF_SHARE_DENY_NONE = 0x40;
        public static IntPtr HFILE_ERROR = new IntPtr(-1);
        //OpenXml中涉及Excel的元素,定义全局变量,方便长时间读写Excel文档
        //SpreadsheetDocument:Excel文档的变量类型
        private SpreadsheetDocument spreadsheetDocument = null;
        //WorkbookPart:Excel文档的总管
        private WorkbookPart workbookPart = null;
        //表格格式总管
        private Stylesheet stylesheet = null;
        //文件路径+名字
        private string path;
        //程序运行时,打开通过文件流载入的Excel文件会提示只读
        private FileStream fileStream;
        //指定Excel的路径(包含名称)
        public OXExcel(string path)
        {
            this.path = path;
        }
        //载入Excel
        public bool LoadExcel()
        {
            //Excel是否存在
            if (System.IO.File.Exists(path))
            {
                //Excel是否只读
                if (!IsReadOnly())
                {
                    //使用文件流载入Excel,使得程序运行时,使用本程序外的软件打开Excel时,将会提示Excel只读,不能编辑。
                    //可以防止在程序外部打开Excel,导致本程序失去对Excel的载入,从而程序崩溃、异常。
                    fileStream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
                    //OpenSettings:Excel打开后的设置
                    OpenSettings openSettings = new OpenSettings();
                    //设置Excel不自动保存
                    openSettings.AutoSave = false;
                    spreadsheetDocument = SpreadsheetDocument.Open(fileStream, true, openSettings);
                    workbookPart = spreadsheetDocument.WorkbookPart;
                    //下列子元素若不存在,则需要重新创建Excel
                    if (workbookPart == null || workbookPart.Workbook == null || workbookPart.Workbook.Sheets == null ||
                        workbookPart.SharedStringTablePart == null || workbookPart.SharedStringTablePart.SharedStringTable == null ||
                        workbookPart.WorkbookStylesPart == null || workbookPart.WorkbookStylesPart.Stylesheet == null)
                    {
                        createExcel();
                    }
                    stylesheet = workbookPart.WorkbookStylesPart.Stylesheet;
                }
                else
                {
                    //只读
                    return false;
                }
            }
            else
            {
                createExcel();
            }
            return true;
        }
        //判断指定路径的Excel是否只读
        private bool IsReadOnly()
        {
            IntPtr vHandle = _lopen(path, OF_READWRITE | OF_SHARE_DENY_NONE);
            if (vHandle == HFILE_ERROR)
            {
                CloseHandle(vHandle);
                return true;
            }
            CloseHandle(vHandle);
            return false;
        }
        //创建Excel
        private void createExcel()
        {
            //SpreadsheetDocument
            if (spreadsheetDocument == null)
            {
                //以path中所指定的地址和名称创建一个Excel文档
                //SpreadsheetDocumentType.Workbook:指明创建的文件类型为后缀名为.xlsx的Excel文档
                spreadsheetDocument = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook);
            }
            //WorkbookPart
            workbookPart = spreadsheetDocument.WorkbookPart;
            if (workbookPart == null)
            {
                workbookPart = spreadsheetDocument.AddWorkbookPart();
            }
            //Workbook
            if (workbookPart.Workbook == null)
            {
                workbookPart.Workbook = new Workbook();
            }
            //Sheets
            if (workbookPart.Workbook.Sheets == null)
            {
                workbookPart.Workbook.Sheets = new Sheets();
            }
            //SharedStringTablePart
            if (workbookPart.SharedStringTablePart == null)
            {
                workbookPart.AddNewPart();
            }
            //SharedStringTabel
            if (workbookPart.SharedStringTablePart.SharedStringTable == null)
            {
                workbookPart.SharedStringTablePart.SharedStringTable = new SharedStringTable();
            }
            workbookPart.SharedStringTablePart.SharedStringTable.Save();
            //WorkbookStylesPart
            if (workbookPart.WorkbookStylesPart == null)
            {
                workbookPart.AddNewPart();
            }
            //Stylesheet
            if (workbookPart.WorkbookStylesPart.Stylesheet == null)
            {
                workbookPart.WorkbookStylesPart.Stylesheet = new Stylesheet();
            }
            workbookPart.Workbook.Save();
            //经过我的大量实践,在创建Workbook和Worksheet后,均需调用spreadsheetDocument.Dispose()方法,否则Excel文件会有问题
            //但是Dispose()后,所有资源均释放了,所以需要重新载入Excel
            //也许有人会想,为什么不在所有关于Excel的操作(包括创建、读写)完成后,再调用Dispose()?
            //我的回答是:程序总会出现异常,异常时,并不会自动调用Dispose
            //请原谅我的强迫症,1、为了能在程序运行时,保持对Excel的载入。2、遇到程序异常时,Excel文件不会出现任何问题。
            reLoad();
        }
        //释放定义的全局变量等所有资源,并重新载入Excel
        private void reLoad()
        {
            Dispose();
            LoadExcel();
        }
        //判断Worksheet是否存在
        public bool IsWorksheetExist(string sheetName)
        {
            //workbookPart:管理Workbook工作薄
            //workbookPart.Workbook.Sheets:Workbook元素中的Sheets元素
            //Sheets.Elements():Sheets元素中Sheet元素的集合
            //Elements().FirstOrDefault():Sheet元素集合中第一个Sheet元素或默认的Sheet元素
            //FirstOrDefault(s => s != null && s.Name != null && s.Name == sheetName)等同于如下代码
            //foreach(Sheet s in workbookPart.Workbook.Sheets.Elements())
            //{
            //    寻找Name与sheetName相同的Sheet元素
            //    if(s! = null && s.Name != null && s.Name == sheetName)
            //    {
            //       sheet = s;
            //       break;
            //    }
            //}
            //Sheet是工作表在宏观上的元素,它存储工作表的标识和名称
            Sheet sheet = workbookPart.Workbook.Sheets.Elements().
                FirstOrDefault(s => s != null && s.Name != null && s.Name == sheetName);
            if (sheet == null)
            {
                return false;
            }
            //WorksheetPart:管理工作表,一个WorksheetPart对应一个工作表
            //Worksheet是工作表在微观上的元素,它主要存储工作表中非空表格的内容
            WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
            //Worksheet,SheetData
            if (worksheetPart.Worksheet == null || worksheetPart.Worksheet.Elements().FirstOrDefault() == null)
            {
                return false;
            }
            return true;
        }
        //添加一个工作表
        public void AddWorksheetPart(string sheetName)
        {
            Sheet sheet = workbookPart.Workbook.Sheets.Elements().
                FirstOrDefault(s => s != null && s.Name != null && s.Name == sheetName);
            WorksheetPart worksheetPart = null;
            if (sheet != null)
            {
                worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
            }
            else
            {
                worksheetPart = workbookPart.AddNewPart();
            }
            //Worksheet
            if (worksheetPart.Worksheet == null)
            {
                worksheetPart.Worksheet = new Worksheet();
            }
            //SheetData
            if (worksheetPart.Worksheet.Elements().FirstOrDefault() == null)
            {
                //workbookPart.AddNewPart():添加一个新的WorksheetPart
                worksheetPart.Worksheet.AppendChild(new SheetData());
            }
            worksheetPart.Worksheet.Save();
            //Sheet
            if (sheet == null)
            {
                sheet = workbookPart.Workbook.Sheets.Elements().LastOrDefault();
                //定义默认的sheetId值,当工作薄中一个工作表也没有时,便以1作为新添加的Sheet元素的Id值
                UInt32Value sheetId = 1;
                if (sheet != null)
                {
                    sheetId = sheet.SheetId + 1;
                }
                //AppendChild:添加一个子元素
                workbookPart.Workbook.Sheets.AppendChild(new Sheet()
                {
                    //Id为字符串类型的Id属性
                    Id = workbookPart.GetIdOfPart(worksheetPart),
                    //SheetId为数值类型的Id属性
                    SheetId = sheetId,
                    //工作表的名称
                    Name = sheetName
                });
            }
            workbookPart.Workbook.Save();
            //此处解释同上方CreateExcel()方法中一致
            reLoad();
        }
        //获取工作表
        public WorksheetPart GetWorksheetPart(string sheetName)
        {
            //WorksheetPart
            Sheet sheet = workbookPart.Workbook.Sheets.Elements().FirstOrDefault(s => s != null && s.Name != null && s.Name == sheetName);
            WorksheetPart worksheetPart = null;
            if (sheet != null)
            {
                worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
            }
            else
            {
                return null;
            }
            //Worksheet,SheetData
            if (worksheetPart.Worksheet == null || worksheetPart.Worksheet.Elements().FirstOrDefault() == null)
            {
                return null;
            }
            return worksheetPart;
        }
        //获取共享字符串表
        public SharedStringTable GetSharedStringTable()
        {
            return workbookPart.SharedStringTablePart.SharedStringTable;
        }
        //根据背景颜色获取自定义的表格样式的索引,不存在则创建
        public int GetStyleIndexByBackColor(System.Drawing.Color color)
        {
            checkStylesheet();
            int fillIndex = getFillIndex(color);
            int index = 0;
            foreach (CellFormat cellFormat in stylesheet.CellFormats.Elements())
            {
                if (cellFormat != null && cellFormat.FillId != null && cellFormat.FillId == fillIndex
                    && cellFormat.FontId == null && cellFormat.BorderId == null)
                {
                    return index;
                }
                index++;
            }
            stylesheet.CellFormats.AppendChild(new CellFormat()
            {
                FillId = (uint)fillIndex,
            });
            stylesheet.Save();
            return index;
        }
        //检验Stylesheet的完整性
        private void checkStylesheet()
        {
            if (stylesheet.Fonts == null)
            {
                stylesheet.Fonts = new Fonts();
            }
            if (stylesheet.Fonts.ChildElements.Count == 0)
            {
                stylesheet.Fonts.AppendChild(new Font());
            }
            if (stylesheet.Fills == null)
            {
                stylesheet.Fills = new Fills();
            }
            if (stylesheet.Fills.ChildElements.Count == 0)
            {
                stylesheet.Fills.AppendChild(new Fill()
                {
                    PatternFill = new PatternFill()
                    {
                        PatternType = PatternValues.None
                    }
                });
                stylesheet.Fills.AppendChild(new Fill()
                {
                    PatternFill = new PatternFill()
                    {
                        PatternType = PatternValues.Gray125
                    }
                });
            }
            if (stylesheet.Borders == null)
            {
                stylesheet.Borders = new Borders();
            }
            if (stylesheet.Borders.ChildElements.Count == 0)
            {
                stylesheet.Borders.AppendChild(new Border());
            }
            if (stylesheet.CellFormats == null)
            {
                stylesheet.CellFormats = new CellFormats();
                if (stylesheet.CellFormats.ChildElements.Count == 0)
                {
                    stylesheet.CellFormats.AppendChild(new CellFormat());
                }
            }
            stylesheet.Save();
        }
        //根据背景颜色获取Stylesheet的子元素Fill的索引值,不存在则创建
        private int getFillIndex(System.Drawing.Color color)
        {
            int fillIndex = 0;
            foreach (Fill fill in stylesheet.Fills.Elements())
            {
                if (fill != null && fill.PatternFill != null && fill.PatternFill.PatternType != null &&
                    fill.PatternFill.PatternType == PatternValues.Solid && fill.PatternFill.ForegroundColor != null &&
                    fill.PatternFill.ForegroundColor.Rgb != null && fill.PatternFill.ForegroundColor.Rgb.Value != null &&
                    fill.PatternFill.ForegroundColor.Rgb.Value == color.ToArgb().ToString("X"))
                {
                    return fillIndex;
                }
                fillIndex++;
            }
            stylesheet.Fills.AppendChild(new Fill()
            {
                PatternFill = new PatternFill()
                {
                    PatternType = PatternValues.Solid,
                    ForegroundColor = new ForegroundColor()
                    {
                        Rgb = new HexBinaryValue(color.ToArgb().ToString("X"))
                    }
                }
            });
            stylesheet.Save();
            return fillIndex;
        }
        // 释放所有资源
        public void Dispose()
        {
            if (stylesheet != null)
            {
                stylesheet.Save();
                stylesheet = null;
            }
            if (workbookPart != null)
            {
                workbookPart.Workbook.Save();
                workbookPart = null;
            }

            if (spreadsheetDocument != null)
            {
                spreadsheetDocument.Close();
                spreadsheetDocument.Dispose();
                spreadsheetDocument = null;
            }
            if (fileStream != null)
            {
                fileStream.Flush();
                fileStream.Close();
                fileStream.Dispose();
                fileStream = null;
            }
        }
    }
    public class OXSheet
    {
        //定义WorksheetPart子元素节点
        private WorksheetPart worksheetPart = null;
        private SharedStringTable sharedStringTable = null;
        //SheetData:记录非空单元格的位置及内容
        private SheetData sheetData = null;
        //指向当前行Row
        private Row currentRow = null;
        //根据ASCII码获取字母A的int编码值
        private int begin = 'A' - 1;
        public OXSheet(WorksheetPart worksheetPart, SharedStringTable sharedStringTable)
        {
            this.worksheetPart = worksheetPart;
            sheetData = worksheetPart.Worksheet.Elements().FirstOrDefault();
            currentRow = sheetData.Elements().LastOrDefault();
            if (currentRow == null)
            {
                AddRow();
            }
            this.sharedStringTable = sharedStringTable;
        }
        //添加一行
        public void AddRow()
        {
            currentRow = new Row();
            sheetData.AppendChild(currentRow);
        }
        //在当前行中添加一个的普通表格
        public void AddCell(object data)
        {
            addCell(data, -1);
        }
        //在当前行中添加一个指定表格样式的普通表格
        public void AddCell(object data, int styleIndex)
        {
            addCell(data, styleIndex);
        }
        private void addCell(object data, int styleIndex)
        {
            Cell cell = currentRow.AppendChild(new Cell()
            {
                //CellValue:单元格内容
                CellValue = new CellValue() { Text = data.ToString() },
                //DataType:单元格类型
                DataType = CellValues.String,
            });
            if (styleIndex != -1)
            {
                cell.StyleIndex = (uint)styleIndex;
            }
        }
        //在当前行中添加一个类型为共享字符串的表格
        public void AddSharedStringCell(object data)
        {
            addSharedStringCell(data, -1);
        }
        //在当前行中添加一个类型为共享字符串、指定表格样式的表格
        public void AddSharedStringCell(object data, int styleIndex)
        {
            addSharedStringCell(data, styleIndex);
        }
        private void addSharedStringCell(object data, int styleIndex)
        {
            Cell cell = currentRow.AppendChild(new Cell()
            {
                CellValue = new CellValue(
                    Convert.ToString(getSharedStringItemIndex(data.ToString()))),
                DataType = CellValues.SharedString,
            });
            if (styleIndex != -1)
            {
                cell.StyleIndex = (uint)styleIndex;
            }
        }
        //获取指定字符串在SharedStringTable中的索引值,不存在就创建
        private int getSharedStringItemIndex(string value)
        {
            //字符串从0开始标记
            int index = 0;
            //寻找是否有与value相同的字符串,若有,则将index设置为对应的标记值,并返回
            //SharedStringItem:共享字符串的数据类型
            //sharedStringTable:共享字符串表
            foreach (SharedStringItem item in sharedStringTable.Elements())
            {
                if (item.InnerText == value)
                {
                    return index;
                }
                index++;
            }
            //若没有与value相同的字符串,则添加一个字符串到共享字符串表中,并将其内容设置为value
            sharedStringTable.AppendChild(new SharedStringItem(new Text(value)));
            sharedStringTable.Save();
            return index;
        }
        //在当前行中添加一个超链接表格
        public void AddHyperLink(object data)
        {
            addHyperlink(data, -1);
        }
        //在当前行中添加一个超链接表格
        public void AddHyperLink(object data, int styleIndex)
        {
            addHyperlink(data, styleIndex);
        }
        private void addHyperlink(object data, int styleIndex)
        {
            Cell cell = currentRow.AppendChild(new Cell()
            {
                CellFormula = new CellFormula() { Text = string.Format("HYPERLINK({0}{1}{0},{0}{1}{0})", "\"", data.ToString()) },
                CellValue = new CellValue(data.ToString()),
                DataType = CellValues.String,
            });
            if (styleIndex != -1)
            {
                cell.StyleIndex = (uint)styleIndex;
            }
        }
        //指定行和列定位表格,写入数据
        public void Write(int rowIndex, int colIndex, object data)
        {
            Cell cell = getCell(getRow(rowIndex), colIndex);
            cell.CellValue = new CellValue(data.ToString());
        }
        //读取指定行和列的内容
        public string Read(int rowIndex, int colIndex)
        {
            Row row = getRow(rowIndex);
            Cell cell = getCell(row, colIndex);
            string value = string.Empty;
            if (cell.CellValue != null && cell.CellValue.InnerText != null)
            {
                value = cell.CellValue.InnerText;
                if (cell.DataType != null && cell.DataType == CellValues.SharedString)
                {
                    return sharedStringTable.ElementAt(Int32.Parse(value)).InnerText;
                }
                else
                {
                    return value;
                }
            }
            return value;
        }
        //根据行索引值获取列Row
        private Row getRow(int rowIndex)
        {
            Row row = sheetData.Elements().FirstOrDefault(r => r != null && r.RowIndex != null && r.RowIndex == rowIndex);
            if (row == null)
            {
                row = new Row() { RowIndex = (uint)rowIndex };
                Row lastRow = sheetData.Elements().LastOrDefault();
                if ((lastRow != null && lastRow.RowIndex != null && lastRow.RowIndex < rowIndex) || lastRow == null)
                {
                    sheetData.AppendChild(row);
                }
                else
                {
                    Row refRow = sheetData.Elements().FirstOrDefault(r => r != null && r.RowIndex != null && r.RowIndex > rowIndex);
                    sheetData.InsertBefore(row, refRow);
                }
            }
            return row;
        }
        //根据指定行和列索引获取单元格Cell
        private Cell getCell(Row row, int colIndex)
        {
            Cell cell = row.Elements().FirstOrDefault(c => c != null && c.CellReference != null && c.CellReference.Value == getColNameByColIndex(colIndex) + row.RowIndex);
            if (cell == null)
            {
                cell = new Cell() { CellReference = getColNameByColIndex(colIndex) + row.RowIndex, DataType = CellValues.String };

                Cell lastCell = row.Elements().LastOrDefault();
                if ((lastCell != null && getColIndexByCellReference(lastCell.CellReference.Value) < colIndex) || lastCell == null)
                {
                    row.AppendChild(cell);
                }
                else
                {
                    Cell nextCell = row.Elements().FirstOrDefault(c => c != null && c.CellReference != null && getColIndexByCellReference(c.CellReference.Value) > colIndex);
                    row.InsertBefore(cell, nextCell);
                }
            }
            return cell;
        }
        //根据第一行中的表格内容,通过与给定的字符串进行匹配获取列的索引值
        public int GetColIndexByHeaderName(string headerName)
        {
            int index = 1;
            Row row = getRow(1);
            foreach (Cell cell in row.Elements())
            {
                if (cell != null && cell.CellValue != null && cell.CellValue.InnerText != null &&
                    cell.CellValue.InnerText == headerName)
                {
                    return index;
                }
                index++;
            }
            row.AppendChild(new Cell() { CellValue = new CellValue() { Text = headerName.ToString() } });
            return index;
        }
        //根据表格的索引值(如A1)获取列的索引值
        private int getColIndexByCellReference(string cellReference)
        {
            System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"[A-Z]{1,}");
            System.Text.RegularExpressions.Match match = regex.Match(cellReference);
            string value = match.Value;
            return getColIndexByColName(value);
        }
        //根据列的名称(如A)获取列的索引值
        private int getColIndexByColName(string colName)
        {
            int index = 0;
            char[] names = colName.ToCharArray();
            int length = names.Length;
            for (int i = 0; i < length; i++)
            {
                index += (names[i] - begin) * (int)Math.Pow(26, length - i - 1);
            }
            return index;
        }
        //根据列的索引值获取列名(如A)
        private string getColNameByColIndex(int index)
        {
            string colName = "";
            if (index < 0)
            {
                return colName;
            }
            while (index > 26)
            {
                colName += ((char)(index % 26 + begin)).ToString();
                index = index / 26;
            }
            colName += ((char)(index % 26 + begin)).ToString();
            return colName;
        }
        //保存工作表
        public void Save()
        {
            worksheetPart.Worksheet.Save();
        }
    }
}

你可能感兴趣的:(C#)