Excel文檔處理之Open XML

引言

在使用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();
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章