引言
在使用Open XML處理Excel文檔之前,需要先確定它的作用範圍,Open XML適用於2007版本及以上的Excel文檔的處理。
第二步就是配置Open XML的開發環境:Open XML相關下載鏈接。
1、新建Excel文檔
咱們先從一個實際的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名稱。
2、添加一個sheet工作表
倘若在Excel中添加一個工作表,並將其命名爲“測試”,會有什麼變化呢?具體的操作要麼把Excel文件後綴名恢復原樣,要麼可以再新建一個Excel,並添加一個工作表。再次打開workbook.xml文件時,會發現多了一個sheet元素。該sheet元素對應的r:id和sheetId的值都比默認的sheet元素的值增加了1,name屬性的值爲“測試”。
添加一個工作表等價的代碼如下:
//本代碼段爲簡化代碼,只作理解之用。
//workbookPart:管理Workbook工作薄
//WorksheetPart:管理工作表,一個WorksheetPart對應一個工作表
//workbookPart.AddNewPart<WorksheetPart>():添加一個新的WorksheetPart
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
//workbookPart.Workbook.Sheets:Workbook元素中的Sheets元素
//Sheets.Elements<Sheet>():Sheets元素中Sheet元素的集合
//Elements<Sheet>().LastOrDefault():Sheet元素集合中最後一個Sheet元素或默認的Sheet元素
Sheet sheet = workbookPart.Workbook.Sheets.Elements<Sheet>().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中也就沒有記錄。
這時,對空白的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<SheetData>().FirstOrDefault();
//Row:工作表中的行
Row row = sheetData.Elements<Row>().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<SharedStringItem>())
{
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分別表示左右上下邊框的樣式。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
});
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<SharedStringTablePart>();
}
//SharedStringTabel
if (workbookPart.SharedStringTablePart.SharedStringTable == null)
{
workbookPart.SharedStringTablePart.SharedStringTable = new SharedStringTable();
}
workbookPart.SharedStringTablePart.SharedStringTable.Save();
//WorkbookStylesPart
if (workbookPart.WorkbookStylesPart == null)
{
workbookPart.AddNewPart<WorkbookStylesPart>();
}
//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<Sheet>():Sheets元素中Sheet元素的集合
//Elements<Sheet>().FirstOrDefault():Sheet元素集合中第一個Sheet元素或默認的Sheet元素
//FirstOrDefault(s => s != null && s.Name != null && s.Name == sheetName)等同於如下代碼
//foreach(Sheet s in workbookPart.Workbook.Sheets.Elements<Sheet>())
//{
// 尋找Name與sheetName相同的Sheet元素
// if(s! = null && s.Name != null && s.Name == sheetName)
// {
// sheet = s;
// break;
// }
//}
//Sheet是工作表在宏觀上的元素,它存儲工作表的標識和名稱
Sheet sheet = workbookPart.Workbook.Sheets.Elements<Sheet>().
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<SheetData>().FirstOrDefault() == null)
{
return false;
}
return true;
}
//添加一個工作表
public void AddWorksheetPart(string sheetName)
{
Sheet sheet = workbookPart.Workbook.Sheets.Elements<Sheet>().
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<WorksheetPart>();
}
//Worksheet
if (worksheetPart.Worksheet == null)
{
worksheetPart.Worksheet = new Worksheet();
}
//SheetData
if (worksheetPart.Worksheet.Elements<SheetData>().FirstOrDefault() == null)
{
//workbookPart.AddNewPart<WorksheetPart>():添加一個新的WorksheetPart
worksheetPart.Worksheet.AppendChild(new SheetData());
}
worksheetPart.Worksheet.Save();
//Sheet
if (sheet == null)
{
sheet = workbookPart.Workbook.Sheets.Elements<Sheet>().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<Sheet>().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<SheetData>().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<CellFormat>())
{
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<Fill>())
{
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<SheetData>().FirstOrDefault();
currentRow = sheetData.Elements<Row>().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<SharedStringItem>())
{
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<Row>().FirstOrDefault(r => r != null && r.RowIndex != null && r.RowIndex == rowIndex);
if (row == null)
{
row = new Row() { RowIndex = (uint)rowIndex };
Row lastRow = sheetData.Elements<Row>().LastOrDefault();
if ((lastRow != null && lastRow.RowIndex != null && lastRow.RowIndex < rowIndex) || lastRow == null)
{
sheetData.AppendChild(row);
}
else
{
Row refRow = sheetData.Elements<Row>().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<Cell>().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<Cell>().LastOrDefault();
if ((lastCell != null && getColIndexByCellReference(lastCell.CellReference.Value) < colIndex) || lastCell == null)
{
row.AppendChild(cell);
}
else
{
Cell nextCell = row.Elements<Cell>().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<Cell>())
{
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();
}
}
}