在使用Open XML处理Excel文档之前,需要先确定它的作用范围,Open XML适用于2007版本及以上的Excel文档的处理。
第二步就是配置Open XML的开发环境:Open XML相关下载链接。
咱们先从一个实际的Excel文档入手,在桌面上新建一个Excel文档,默认会有一个Sheet,可以理解为一个工作表或一个页面。
创建一个Excel所等价的代码如下:
//本代码段为简化代码,只作理解之用。
//SpreadsheetDocument:Excel文档的变量类型
//以path中所指定的地址和名称创建一个Excel文档
//SpreadsheetDocumentType.Workbook:指明创建的文件类型为后缀名为.xlsx的Excel文档
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook);
将刚刚新建的Excel文档的后缀名改为rar(压缩包格式),然后打开压缩包,看到如下图所示的目录,其中xl文件夹中的文件很重要,需要仔细研究。打开xl文件夹。
先打开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中添加一个工作表,并将其命名为“测试”,会有什么变化呢?具体的操作要么把Excel文件后缀名恢复原样,要么可以再新建一个Excel,并添加一个工作表。再次打开workbook.xml文件时,会发现多了一个sheet元素。该sheet元素对应的r:id和sheetId的值都比默认的sheet元素的值增加了1,name属性的值为“测试”。
添加一个工作表等价的代码如下:
//本代码段为简化代码,只作理解之用。
//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
});
在上方压缩包目录图中,打开worksheets文件夹。该目录下有一个名为“sheet1.xml"的文件,对应默认的”Sheet1“工作表。有多少个工作表,这个文件夹里面就有多少个sheet文件,sheet文件对所有非空表格的信息都进行了登记。
打开sheet1.xml文件,内容如下图所示。其中sheetViews元素可以描述当前选中的表格所在的位置,由于新建的Excel文档没有选中表格,所有sheetViews没有描述;sheetData元素记录非空表格的位置及表格内容,同样,由于Excel没有写入任何内容,sheetData中也就没有记录。
这时,对空白的Sheet1工作表添加如下图所示的内容。分别在A1、B2、C3单元格添加了“1”、“数据”、“Data”,并选中B1单元格。添加这些数据后,sheet1.xml文件主要的变化如下图。sheetViews元素中,selection元素描述选中的单元格为B1。sheetData元素中,有三个row元素,分别对应添加的“1”、“数据”、“Data”;在row元素中,r属性表示第几行,c元素记录非空表格的位置及表格内容;在c元素中,r元素表示单元格的位置,其中第一个c元素中,v元素表示单元格的内容,第二个和第三个c元素的解释请看下面的内容。
在xl文件夹中,多了一个名为sharedStrings.xml的文件,这个文件名字面上的意思是共享字符串,这是Excel中很有意思的一个东西,它将可能会重复用到的字符串都存储于这个文件中。打开它,内容如下图所示,si元素中存储着可能会重复用到的字符串,可以想象,当N个单元格中的内容都是“数据”时,那么Excel就不用为每个单元格分配额外的存储空间,而只需要在上图中的v元素中存储该字符串的引用即可,例:在下图中,数据在sharedStrings.xml文件中的位置是第0个(默认从第0个开始),上图中c元素中t属性赋值为“s”,表明该单元格的内容为共享字符串,v元素中的值为0,表示该单元格中的内容为sharedStrings.xml文件中第0个字符串。
向表格中添加内容的代码如下:
//本代码段为简化代码,只作理解之用。
//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;
}
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属性调用。
上述添加单元格样式的代码如下:
//本代码段为简化代码,只作理解之用。
//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
});
代码分为两个部分: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();
}
}
} | | | |