1 HSSFSheet sheet = hssfworkbook.CreateSheet("new sheet"); 2 3 4 5 // Create a row and put some cells in it. Rows are 0 based. 6 7 HSSFRow row = sheet.CreateRow(1); 8 9 // Create a cell and put a value in it. 10 11 HSSFCell cell = row.CreateCell(1); 12 13 // Style the cell with borders all around. 14 15 HSSFCellStyle style = hssfworkbook.CreateCellStyle(); 16 17 style.BorderBottom= HSSFCellStyle.BORDER_THIN; 18 19 style.BorderLeft= HSSFCellStyle.BORDER_THIN; 20 21 style.BorderRight= HSSFCellStyle.BORDER_THIN; 22 23 style.BorderTop = HSSFCellStyle.BORDER_THIN ; 24 25 cell.CellStyle= style;
NPOI使用手冊(此文章為轉載)
本章將介紹NPOI的一些基本信息,包括以下幾個部分
- 什么是NPOI
- 版權說明
- 相關資源
- 團隊介紹
- 未來展望
- 各Assembly的作用
1.1 什么是NPOI
NPOI,顧名思義,就是POI的.NET版本。那POI又是什么呢?POI是一套用Java寫成的庫,能夠幫助開發者在沒有安裝微軟Office的情況下讀寫Office 97-2003的文件,支持的文件格式包括xls, doc, ppt等。在本文發布時,POI的最新版本是3.5 beta 6。
NPOI 1.x是基於POI 3.x版本開發的,與poi 3.2對應的版本是NPOI 1.2,目前最新發布的版本是1.2.1,在該版本中僅支持讀寫Excel文件和Drawing格式,其他文件格式將在以后的版本中得到支持。
1.2 版權說明
NPOI采用的是Apache 2.0許可證(poi也是采用這個許可證),這意味着它可以被用於任何商業或非商業項目,你不用擔心因為使用它而必須開放你自己的源代碼,所以它對於很多從事業務系統開發的公司來說絕對是很不錯的選擇。
當然作為一個開源許可證,肯定也是有一些義務的,例如如果你在系統中使用NPOI,你必須保留NPOI中的所有聲明信息。對於源代碼的任何修改,必須做出明確的標識。
完整的apache 2.0許可證請見http://www.phpx.com/man/Apache-2/license.html
1.3 相關資源
官方網站:http://npoi.codeplex.com/
POIFS Browser 1.2
下載地址:http://npoi.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24305
QQ交流群: 78142590
1.4 團隊介紹
Tony Qu來自於中國上海,是這個項目的發起人和開發人員,時區是GMT+8,2008年9月開始了NPOI的開發,負責NPOI所有底層庫的開發、測試和bug修復。
個人blog地址為http://tonyqus.cnblogs.com/
Hüseyin Tüfekçilerli來自於土耳其的伊斯坦布爾,也是這個項目的開發人員,時區是GMT+2,2008年11月參與了NPOI的開發,主要負責POIFS Browser 1.0的開發工作。
個人blog地址為http://huseyint.com/
aTao.Xiang,來自中國,2009年8月開始參與該項目,主要參與了NPOI 1.2中文版的撰寫工作和推廣工作
個人blog地址為http://www.cnblogs.com/atao/
1.5 回顧與展望
目前POI版本中的HWPF(用於Word的讀寫庫)還不是很穩定,並非正式發布版本,且負責HWPF的關鍵開發人員已經離開,所以NPOI可能考慮自己重新開發HWPF。另外,目前微軟正在開發Open XML Format SDK,NPOI可能會放棄對ooxml的支持,當然這取決於用戶的需求和Open XML Format SDK的穩定性和速度。從目前而言,NPOI有幾大優勢
第一,完全基於.NET 2.0,而非.NET 3.0/3.5。
第二,讀寫速度快(有個國外的兄弟回復說,他原來用ExcelPackage生成用了4-5個小時,現在只需要4-5分鍾)
第三,穩定性好(相對於用Office OIA而言,畢竟那東西是基於Automation做的,在Server上跑個Automation的東西,想想都覺得可怕),跑過了將近1000個測試用例(來自於POI的testcase目錄)
第四,API簡單易用,當然這得感謝POI的設計師們
第五,完美支持Excel 2003格式(據說myxls無法正確讀取xls模板,但NPOI可以),以后也許是所有Office 2003格式
希望NPOI把這些優勢繼續發揚下去,這樣NPOI才會更有競爭力。
1.6 NPOI 1.2中各Assembly的作用
NPOI目前有好幾個assembly,每個的作用各有不同,開發人員可以按需加載相應的assembly。在這里大概羅列一下:
NPOI.Util 基礎輔助庫 NPOI.POIFS OLE2格式讀寫庫 NPOI.DDF Microsoft Drawing格式讀寫庫 NPOI.SS Excel公式計算庫 NPOI.HPSF OLE2的Summary Information和Document Summary Information屬性讀寫庫 NPOI.HSSF Excel BIFF格式讀寫庫
NPOI 1.2教程 - 2.1.1 創建Workbook和Sheet
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
在開始創建Workbook之前,我們先要在項目中引用一些必要的NPOI assembly,如下所示:
NPOI.dll
NPOI.POIFS.dll
NPOI.HSSF.dll
NPOI.Util.dll
1 HSSFWorkbook hssfworkbook = new HSSFWorkbook(); 2 3 ////create a entry of DocumentSummaryInformation 4 DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation(); 5 dsi.Company = "NPOI Team"; 6 hssfworkbook.DocumentSummaryInformation = dsi; 7 8 ////create a entry of SummaryInformation 9 SummaryInformation si = PropertySetFactory.CreateSummaryInformation(); 10 si.Subject = "NPOI SDK Example"; 11 hssfworkbook.SummaryInformation = si; 12 13 //here, we must insert at least one sheet to the workbook. otherwise, Excel will say 'data lost in file' 14 //So we insert three sheet just like what Excel does 15 hssfworkbook.CreateSheet("Sheet1"); 16 hssfworkbook.CreateSheet("Sheet2"); 17 hssfworkbook.CreateSheet("Sheet3"); 18 hssfworkbook.CreateSheet("Sheet4"); 19 20 ((HSSFSheet)hssfworkbook.GetSheetAt(0)).AlternativeFormula = false; 21 ((HSSFSheet)hssfworkbook.GetSheetAt(0)).AlternativeExpression = false; 22 23 //Write the stream data of workbook to the root directory 24 FileStream file = new FileStream(@"test.xls", FileMode.Create); 25 hssfworkbook.Write(file); 26 file.Close();
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
1 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 2 3 HSSFRow row1=sheet1.CreateRow(0); 4 5 row1.CreateCell(0).SetCellValue(1); 6 7 8 sheet1.CreateRow(0).CreateCell(0).SetCellValue("This is a Sample"); 9 10 //這么用有個前提,那就是第0行還沒創建過,否則得這么用: 11 12 //sheet1.GetRow(0).CreateCell(0).SetCellValue("This is a Sample"); 13 14 15 16 //如果你要獲得某一個已經創建的單元格對象,可以用下面的代碼: 17 18 sheet1.GetRow(row_index).GetCell(column_index);
相關范例請見 NPOI 1.2正式版中的SetCellValuesInXls項目。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
批注主要有三個屬性需要設置,一個是批注的位置和大小、一個是批注的文本、還有一個是批注的作者。
批注的位置和大小,在Excel中是與單元格密切相關的,NPOI中通過HSSFClientAnchor的實例來表示,它的構造函數比較復雜,有8個參數,它們分別是
| 參數 |
說明 |
| dx1 |
第1個單元格中x軸的偏移量 |
| dy1 |
第1個單元格中y軸的偏移量 |
| dx2 |
第2個單元格中x軸的偏移量 |
| dy2 |
第2個單元格中y軸的偏移量 |
| col1 |
第1個單元格的列號 |
| row1 |
第1個單元格的行號 |
| col2 |
第2個單元格的列號 |
| row2 |
第2個單元格的行號 |
1 //例如,如果我們打算讓注釋顯示在B3和E5之間,就應該這么寫: 2 3 HSSFPatriarch patr = sheet.CreateDrawingPatriarch(); 4 5 HSSFComment comment1 = patr.CreateComment(new HSSFClientAnchor(0, 0, 0, 0, 1, 2 , 4, 4)); 6 7 //下面我們設置這個批注的內容和作者,這個比較簡單: 8 9 comment1.String = new HSSFRichTextString("Hello World"); 10 11 comment1.Author = "NPOI Team"; 12 13 //最后一步就是把批注賦給某個單元格: 14 15 HSSFCell cell = sheet.CreateRow(1).CreateCell(1); 16 17 cell.CellComment = comment1;
對於批注,你有兩種選擇,一種是隱藏(默認),一種是顯示(即表單一打開就顯示該批注),可以通過comment1.Visible屬性來控制。
看了上面這張圖大家就應該明白了,這里有2個批注,下面那個是顯示的,上面那個是隱藏的。
相關范例請見 NPOI 1.2正式版中的SetCellCommentInXls。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
NPOI中主要是靠HSSFSheet.Header和HSSFSheet.Footer來設置的,這兩個屬性分別是HSSFHeader和HSSFFooter類型的。
參考代碼如下:
1 HSSFSheet s1 = hssfworkbook.CreateSheet("Sheet1"); 2 3 s1.CreateRow(0).CreateCell(1).SetCellValue(123); 4 5 6 7 //set header text 8 9 s1.Header.Center = "This is a test sheet"; 10 11 //set footer text 12 13 s1.Footer.Left = "Copyright NPOI Team"; 14 15 s1.Footer.Right = "created by Tony Qu(瞿傑)";
本范例完整代碼請見NPOI.Examples中的CreateHeaderFooterInXls項目。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
使用NPOI時要注意,所有的格式都是通過CellStyle.DataFormat賦給單元格的,而不是直接賦給單元格。
案例一 日期格式
假設我們現在需要顯示的日期的格式為2008年5月5日,可以用下面的代碼生成:
1 HSSFSheet sheet = hssfworkbook.CreateSheet("new sheet"); 2 3 HSSFCell cell = sheet.CreateRow(0).CreateCell(0); 4 5 cell.SetCellValue(new DateTime(2008,5,5)); 6 7 //set date format 8 9 HSSFCellStyle cellStyle = hssfworkbook.CreateCellStyle(); 10 11 HSSFDataFormat format = hssfworkbook.CreateDataFormat(); 12 13 cellStyle.DataFormat = format.GetFormat("yyyy年m月d日"); 14 15 cell.CellStyle=cellStyle;
由於這里的“yyyy年m月d日”屬於自定義格式(區別於Excel內嵌的格式),所以必須用hssfworkbook.CreateDataFormat()創建一個HSSFDataFormat實例,然后使用format.GetFormat來獲取相應的格式,只要是Excel支持的格式表示方式,這種方式都能夠實現。
案例二保留2位小數
假設我們有個單元格的值為1.2,怎么顯示成1.20呢?在Excel中可以用“0.00”來表示,所以下面的代碼就能完成:
1 // Create a row and put some cells in it. Rows are 0 based. 2 3 HSSFCell cell = sheet.CreateRow(0).CreateCell(0); 4 5 //set value for the cell 6 7 cell.SetCellValue(1.2); 8 9 //number format with 2 digits after the decimal point - "1.20" 10 11 HSSFCellStyle cellStyle = hssfworkbook.CreateCellStyle(); 12 13 cellStyle.DataFormat = HSSFDataFormat.GetBuiltinFormat("0.00"); 14 15 cell.CellStyle = cellStyle;
這里與上面有所不同,用的是HSSFDataFormat.GetBuiltinFormat()方法,之所以用這個,是因為0.00是Excel內嵌的格式,完整的Excel內嵌格式列表大家可以看這個窗口中的自定義列表:
這里就不一一列出了。
案例三貨幣格式
貨幣格式在金融的項目中經常用到,比如說人民幣符號¥,美元符號$等,這里可以用下面的代碼表示:
HSSFCell cell2 = sheet.CreateRow(1).CreateCell(0); cell2.SetCellValue(20000); HSSFCellStyle cellStyle2 = hssfworkbook.CreateCellStyle(); HSSFDataFormat format = hssfworkbook.CreateDataFormat(); cellStyle2.DataFormat = format.GetFormat("¥#,##0"); cell2.CellStyle = cellStyle2;
注意,這里還加入了千分位分隔符,所以是#,##,至於為什么這么寫,你得去問微軟,呵呵。
案例四百分比
百分比在報表中也很常用,其實基本上和上面一樣,只是格式表示是0.00%,代碼如下:
cellStyle4.DataFormat = HSSFDataFormat.GetBuiltinFormat("0.00%");
由於這里是內嵌格式,所以直接用HSSFDataFormat.GetBuiltinFormat即可。
案例五中文大寫
在表示金額時,我們時常會用到,我也見過不少兄弟實現了數字轉中文大小寫的工具類,以后你可以嘗試讓Excel去處理這一切,代碼和剛才差不多,也是改格式的表示:
HSSFDataFormat format = hssfworkbook.CreateDataFormat();
cellStyle6.DataFormat = format.GetFormat("[DbNum2][$-804]0");
由於是自定義格式,所以用了HSSFDataFormat.GetFormat,相信你對這兩種獲取格式的形式的區別越來越熟悉了。
案例六科學計數法
這東西數學課上我們都學過,雖然用的不多,但是既然Excel支持,這里也提一下:
cellStyle3.DataFormat = HSSFDataFormat.GetBuiltinFormat("0.00E+00");
下面展示下以上這些例子的顯示效果:
最后總結一下HSSFDataFormat.GetFormat和HSSFDataFormat.GetBuiltinFormat的區別:
當使用Excel內嵌的(或者說預定義)的格式時,直接用HSSFDataFormat.GetBuiltinFormat靜態方法即可。
當使用自己定義的格式時,必須先調用HSSFWorkbook.CreateDataFormat(),因為這時在底層會先找有沒有匹配的內嵌FormatRecord,如果沒有就會新建一個FormatRecord,所以必須先調用這個方法,然后你就可以用獲得的HSSFDataFormat實例的GetFormat方法了,當然相對而言這種方式比較麻煩,所以內嵌格式還是用HSSFDataFormat.GetBuiltinFormat靜態方法更加直接一些。不過自定義的格式也不是天馬行空隨便定義,還是要參照Excel的格式表示來定義,具體請看相關的Excel教程。
注意:自定義的FormatRecord是嵌入xls文件內部的,所以不用擔心對方Excel中有沒有定義過這種格式,都是能夠正常使用的。
相關范例請參考NPOI 1.2正式版中的NumberFormatInXls項目。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
合並單元格在制作表格時很有用,比如說表格的標題就經常是把第一行的單元格合並居中。那么在NPOI中應該如何實現單元格的合並呢?
為了實現這一功能,NPOI引入了新的概念,即Region,因為合並單元格,其實就是設定一個區域。下面說一下Region類的參數,Region總共有4個參數,如下所示
| Region的參數 |
說明 |
| FirstRow |
區域中第一個單元格的行號 |
| FirstColumn |
區域中第一個單元格的列號 |
| LastRow |
區域中最后一個單元格的行號 |
| LastColumn |
區域中最后一個單元格的列號 |
由於單元格的合並都是在表的基礎上建立的,所以我們得先建Sheet:
1 HSSFWorkbook hssfworkbook = new HSSFWorkbook();
2 HSSFSheet sheet = hssfworkbook.CreateSheet("new sheet");
接下來我們根據實際場景來做一些演示。
場景一標題行的合並
這種場景是最常見的,比如說我們要建立一張銷售情況表,英文叫Sales Report
我們先設置居中和字體樣式,這里我們采用20號字體,代碼如下:
1 HSSFRow row = sheet.CreateRow(0); 2 3 HSSFCell cell = row.CreateCell(0); 4 5 cell.SetCellValue("Sales Report"); 6 7 HSSFCellStyle style = hssfworkbook.CreateCellStyle(); 8 9 style.Alignment = HSSFCellStyle.ALIGN_CENTER; 10 11 HSSFFont font = hssfworkbook.CreateFont(); 12 13 font.FontHeight = 20*20; 14 15 style.SetFont(font); 16 17 cell.CellStyle = style;
要產生圖中的效果,即把A1:F1這6個單元格合並,然后添加合並區域:
sheet.AddMergedRegion(new Region(0, 0, 0, 5));
場景二多行合並
看完場景一,你可不要認為多行合並就需要一行一行做,其實也只需要一行代碼,比如說我們要把C3:E5合並為一個單元格,那么就可以用下面的代碼:
sheet.AddMergedRegion(new Region(2, 2, 4, 4));
提示 即使你沒有用CreateRow和CreateCell創建過行或單元格,也完全可以直接創建區域然后把這一區域合並,Excel的區域合並信息是單獨存儲的,和RowRecord、ColumnInfoRecord不存在直接關系。
相關范例請參考NPOI 1.2正式版中的MergedCellInXls項目。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
本節將圍繞“對齊”選項卡中的設置展開,雖然實際上你會發現該選項卡中的很多設置和對齊沒有什么關系。合並單元格已經在2.2.2節講過了,這里就不提了。
首先我們用代碼創建必要的單元格,代碼如下:
1 HSSFWorkbook hssfworkbook = new HSSFWorkbook(); 2 3 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 4 5 HSSFRow row = sheet1.CreateRow(0); 6 7 row.CreateCell(0).SetCellValue("Test");
這里我們假設在A0單元格中加入了文本Test。
請注意接下來我們要做的所有操作都是在CellStyle的基礎上完成的,所以我們創建一個HSSFCellStyle:
HSSFCellStyle style=hssfworkbook.CreateCellStyle();
水平對齊
這里用的是HSSFCellStyle.Alignment,默認值自然是常規,即HSSFCellStyle.ALIGN_GENERAL。
如果是左側對齊就是
style.Alignment = HSSFCellStyle.ALIGN_LEFT;
如果是居中對齊就是
style.Alignment = HSSFCellStyle.ALIGN_CENTER;
如果是右側對齊就是
style.Alignment = HSSFCellStyle.ALIGN_RIGHT;
如果是跨列舉中就是
style.Alignment = HSSFCellStyle.ALIGN_CENTER_SELECTION;
如果是兩端對齊就是
style.Alignment = HSSFCellStyle.ALIGN_JUSTIFY;
如果是填充就是
style.Alignment = HSSFCellStyle.ALIGN_FILL;
注意:以上選項僅當有足夠的寬度時才能產生效果,不設置寬度恐怕看不出區別。
垂直對齊
這里用的是HSSFCellStyle.VerticalAlignment,默認值為居中,即HSSFCellStyle.VERTICAL_CENTER
如果是靠上就是
style.VerticalAlignment=HSSFCellStyle.VERTICAL_TOP
如果是居中就是
style.VerticalAlignment=HSSFCellStyle.VERTICAL_CENTER
如果是靠下就是
style.VerticalAlignment=HSSFCellStyle.VERTICAL_BOTTOM
如果是兩端對齊就是
style.VerticalAlignment=HSSFCellStyle.VERTICAL_JUSTIFY
注意:以上選項僅當有足夠的高度時才能產生效果,不設置高度恐怕看不出區別。
自動換行
自動換行翻譯成英文其實就是Wrap的意思,所以這里我們應該用WrapText屬性,這是一個布爾屬性
style.WrapText = true;
效果如下所示:
文本縮進
這是一個不太引人注意的選項,所以這里給張圖出來,讓大家知道是什么,縮進說白了就是文本前面的空白,我們同樣可以用屬性來設置,這個屬性叫做Indention。
style.Indention = 3;
文本旋轉
文本方向大家一定在Excel中設置過,上圖中就是調整界面,主要參數是度數,那么我們如何在NPOI中設置呢?
style.Rotation=(short)90;
以上代碼是把單元格A1中的文本逆時針旋轉90度,等同於下圖中的設置:
請注意,這里的Rotation取值是從-90到90,而不是0-180度。
最后別忘了把樣式變量style賦給HSSFCellStyle.CellStyle,否則就前功盡棄了,呵呵!
以上的一些功能,比如文本旋轉和自動換行,使用傳統的cvs和html法恐怕是無法實現的。隨着學習的不斷深入,你將越來越意識到使用NPOI生成Excel其實如此簡單。
相關范例請參考NPOI 1.2正式版中的SetAlignmentInXls和RotateTextInXls。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
很多表格中都要使用邊框,本節將為你重點講解NPOI中邊框的設置和使用。
邊框和其他單元格設置一樣也是在HSSFCellStyle上操作的,HSSFCellStyle有2種和邊框相關的屬性,分別是:
| 邊框相關屬性 |
說明 |
范例 |
| Border+方向 |
邊框類型 |
BorderTop, BorderBottom,BorderLeft, BorderRight |
| 方向+BorderColor |
邊框顏色 |
TopBorderColor,BottomBorderColor, LeftBorderColor, RightBorderColor |
其中邊框類型分為以下幾種:
| 邊框范例圖 |
對應的靜態值 |
| HSSFCellStyle.BORDER_DOTTED |
|
| HSSFCellStyle.BORDER_HAIR |
|
| HSSFCellStyle.BORDER_DASH_DOT_DOT |
|
| HSSFCellStyle.BORDER_DASH_DOT |
|
| HSSFCellStyle.BORDER_DASHED |
|
| HSSFCellStyle.BORDER_THIN |
|
| HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT |
|
| HSSFCellStyle.BORDER_SLANTED_DASH_DOT |
|
| HSSFCellStyle.BORDER_MEDIUM_DASH_DOT |
|
| HSSFCellStyle.BORDER_MEDIUM_DASHED |
|
| HSSFCellStyle.BORDER_MEDIUM |
|
| HSSFCellStyle.BORDER_THICK |
|
| HSSFCellStyle.BORDER_DOUBLE |
至於顏色那就很多了,全部在HSSFColor下面,如HSSFColor.GREEN, HSSFColor.RED,都是靜態實例,可以直接引用。
下面我們假設我們要把一個單元格的四周邊框都設置上,可以用下面的代碼:
這段代碼使用了最普通的細邊框,使得這個單元格看上去像塊空心磚頭。
注意:這里我們沒有設置邊框的顏色,但這不會影響最終的效果,因為Excel會用默認的黑色給邊框上色。
如果要設置顏色的話,也很簡單,如下:
style.BottomBorderColor= HSSFColor.GREEN.index;
以上代碼將底部邊框設置為綠色,要注意,不是直接把HSSFColor.GREEN賦給XXXXBorderColor屬性,而是把index的值賦給它。
相關范例請參考NPOI 1.2正式版中的SetBorderStyleInXls項目。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
本節我們將繼續使用NPOI來設置單元格格式,這一節我們主要講如何設置“字體”。
在設置字體之前,我們首先要做的就是創建字體對象,這和創建數字格式很相似。
HSSFFont font = hssfworkbook.CreateFont();
這句話會在Excel文件內部創建相應的FontRecord,所以你不用客戶因為自己機器上的Excel沒有相應的字體設置而導致設置丟失。
字體在設置完成后,我們就可以把它賦給單元格樣式,代碼如下:
HSSFCellStyle style1 = hssfworkbook.CreateCellStyle();
style1.SetFont(font);
cell1.CellStyle=style1;
這里的cell1是HSSFCell的一個實例。
好了,下面我們就開始對字體進行設置。
字體名稱
這里的字體名稱是通過HSSFFont.FontName進行設置的,至於具體的名稱,只要是常用字體都可以,比如說Arial, Verdana等,當然也可以是中文字體名,如宋體、黑體等。不過設置字體名稱有個前提,那就是假設打開這個xls文件的客戶機上有這種字體,如果沒有,Excel將使用默認字體。
下面就是設置字體名稱為“宋體”的代碼:
font.FontName = "宋體";
字號
與字號有關的屬性有兩個,一個是FontHeight,一個是FontHeightInPoints。區別在於,FontHeight的值是FontHeightInPoints的20倍,通常我們在Excel界面中看到的字號,比如說12,對應的是FontHeightInPoints的值,而FontHeight要產生12號字體的大小,值應該是240。所以通常建議你用FontHeightInPoint屬性。
如果要設置字號為12,代碼就是
font.FontHeightInPoints = 12;
字體顏色
這里可能會與CellStyle上的ForegroundColor和BackgroundColor產生混淆,其實所有的字體顏色都是在HSSFFont的實例上設置的,CellStyle的ForegroundColor和BackgroundColor分別指背景填充色和填充圖案的顏色,和文本顏色無關。
要設置字體顏色,我們可以用HSSFFont.Color屬性,顏色可以通過HSSFColor獲得,代碼如下所示:
font.Color = HSSFColor.RED.index;
這行代碼把文本設置為紅色。
下划線
通常我們所說的下划線都是單線條的,其實Excel支持好幾種下划線,如下所示:
| 類型 |
對應的值 |
| 單下划線 |
HSSFFont.U_SINGLE |
| 雙下划線 |
HSSFFont.U_DOUBLE |
| 會計用單下划線 |
HSSFFont.U_SINGLE_ACCOUNTING |
| 會計用雙下划線 |
HSSFFont.U_DOUBLE_ACCOUNTING |
| 無下划線 |
HSSFFont.U_NONE |
當你要設置下划線時,可以用HSSFFont.Underline屬性,這是一個byte類型的值,例如
font.Underline=HSSFFont.U_SINGLE
這行代碼就是設置單下划線的代碼。
上標下標
設置這東西可以用HSSFFont.TypeOffset屬性,值有以下幾種:
| TypeOffset的值 |
說明 |
| HSSFFont.SS_SUPER |
上標 |
| HSSFFont.SS_SUB |
下標 |
| HSSFFont.SS_NONE |
普通,默認值 |
所以如果你要上標的話,可以用下面的代碼:
font.TypeOffset=HSSFFont.SS_SUPER;
刪除線
設置這東西可以用HSSFFont.IsStrikeout屬性,當為true時,表示有刪除線;為false則表示沒有刪除線。
相關范例請參考NPOI 1.2正式版中的ApplyFontInXls的項目。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
本節我們將用NPOI來為單元格添加背景和圖案。
在之前的教程中,我們已經提到HSSFCellStyle有兩個背景顏色屬性,一個叫FillBackgroundColor,另一個叫FillForegroundColor,但其實這指的都是背景顏色,那為什么還有ForegroundColor呢?為了能夠幫助大家理解,我們舉一個實際的例子,下面這個圖案是Excel的一個單元格:
線是白色的,背景是紅色的。這里的線其實就是下面的Excel界面中的圖案:
至於線的顏色則是圖案顏色,即白色。
所以以上單元格如果要用NPOI來設置就可以用以下代碼完成:
1 //fill background 2 3 HSSFCellStyle style8 = hssfworkbook.CreateCellStyle(); 4 5 style8.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.WHITE.index; 6 7 style8.FillPattern = HSSFCellStyle.SQUARES; 8 9 style8.FillBackgroundColor = NPOI.HSSF.Util.HSSFColor.RED.index; 10 11 sheet1.CreateRow(7).CreateCell(0).CellStyle = style8;
現在是不是清楚一些了,這里的FillPattern就圖案樣式,所有的枚舉值都是HSSFCellStyle的常量;FillForegroundColor就是圖案的顏色,而FillBackgroundColor則是背景的顏色,即紅色。
下面羅列一下圖案樣式及其對應的值:
| 圖案樣式 |
常量 |
| HSSFCellStyle.NO_FILL |
|
| HSSFCellStyle.ALT_BARS |
|
| HSSFCellStyle.FINE_DOTS |
|
| HSSFCellStyle.SPARSE_DOTS |
|
| HSSFCellStyle.LESS_DOTS |
|
| HSSFCellStyle.LEAST_DOTS |
|
| HSSFCellStyle.BRICKS |
|
| HSSFCellStyle.BIG_SPOTS |
|
| HSSFCellStyle.THICK_FORWARD_DIAG |
|
| HSSFCellStyle.THICK_BACKWARD_DIAG |
|
| HSSFCellStyle.THICK_VERT_BANDS |
|
| HSSFCellStyle.THICK_HORZ_BANDS |
|
| HSSFCellStyle.THIN_HORZ_BANDS |
|
| HSSFCellStyle.THIN_VERT_BANDS |
|
| HSSFCellStyle.THIN_BACKWARD_DIAG |
|
| HSSFCellStyle.THIN_FORWARD_DIAG |
|
| HSSFCellStyle.SQUARES |
|
| HSSFCellStyle.DIAMONDS |
通過這張表,你將很容易找到自己需要的樣式,不用再去一個一個猜測了。
相關范例請參考NPOI 1.2正式版中的ColorfullMatrixTable和FillBackgroundInXls。
NPOI 1.2教程 - 2.2.7 設置單元格的寬度和高度
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
在Excel中,單元格的寬度其實就是列的寬度,因為Excel假設這一列的單元格的寬度肯定一致。所以要設置單元格的寬度,我們就得從列的寬度下手,HSSFSheet有個方法叫SetColumnWidth,共有兩個參數:一個是列的索引(從0開始),一個是寬度。
現在假設你要設置B列的寬度,就可以用下面的代碼:
HSSFWorkbook hssfworkbook = new HSSFWorkbook();
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1");
sheet1.SetColumnWidth(1, 100 * 256);
這里你會發現一個有趣的現象,SetColumnWidth的第二個參數要乘以256,這是怎么回事呢?其實,這個參數的單位是1/256個字符寬度,也就是說,這里是把B列的寬度設置為了100個字符。
剛才說的是如何設置,那如何去讀取一個列的寬度呢?直接用GetColumnWidth方法,這個方法只有一個參數,那就是列的索引號。如下所示:
int col1width = sheet1.GetColumnWidth(1);
說完寬度,我們來說高度,在Excel中,每一行的高度也是要求一致的,所以設置單元格的高度,其實就是設置行的高度,所以相關的屬性也應該在HSSFRow上,它就是HSSFRow.Height和HeightInPoints,這兩個屬性的區別在於HeightInPoints的單位是點,而Height的單位是1/20個點,所以Height的值永遠是HeightInPoints的20倍。
要設置第一行的高度,可以用如下代碼:
sheet1.CreateRow(0).Height = 200*20;
或者
sheet1.CreateRow(0).HeightInPoints = 200;
如果要獲得某一行的行高,可以直接拿HSSFRow.Height屬性的返回值。
你可能覺得一行一行設置行高或者一列一列設置列寬很麻煩,那你可以考慮使用HSSFSheet.DefaultColumnWidth、HSSFSheet.DefaultRowHeight和HSSFSheet.DefaultRowHeightInPoints屬性。
一旦設置了這些屬性,如果某一行或者某一列沒有設置寬度,就會使用默認寬度或高度。代碼如下:
sheet1.DefaultColumnWidth=100*256;
sheet1.DefaultRowHeight=30*20;
相關范例請見NPOI 1.2正式版中的SetWidthAndHeightInXls項目
從這節開始,我們將開始學習Excel高級一點的功能--公式。為某個單元格指定公式后,單元格中的類容將根據公式計算得出,如圖:
圖中設置的是一個基本表達式”1+2*3”,單元格A1中將顯示此表達式計算的結果”7”,如圖所示。對應的C#生成代碼也很簡單,如下:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1");
HSSFRow row1=sheet1.CreateRow(0); HSSFCell cel1 = row1.CreateCell(0); HSSFCell cel2 = row1.CreateCell(1); HSSFCell cel3 = row1.CreateCell(2); cel1.SetCellFormula("1+2*3"); cel2.SetCellValue(5);
同樣,NPOI也支持單元格引用類型的公式設置,如下圖中的C1=A1*B1。
對應的公式設置代碼為:
cel3.SetCellFormula("A1*B1");
是不是很簡單呢?但要注意,在利用NPOI寫程序時,行和列的計數都是從0開始計算的,但在設置公式時又是按照Excel的單元格命名規則來的。
這節我們開始學習Excel中最常用的函數—Sum求和函數。
首先,我們先看一上最簡單的Sum函數:Sum(num1,num2,...)。使用效果如圖
圖中的E1=Sum(A1,C1)表示將A1與C1的和填充在E1處,與公式”E1=A1+C1”等效。對應的生成代碼與上一節中的基本計算公式類似:
1 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 2 HSSFRow row1 = sheet1.CreateRow(0); 3 HSSFCell cel1 = row1.CreateCell(0); 4 HSSFCell cel2 = row1.CreateCell(1); 5 HSSFCell cel3 = row1.CreateCell(2); 6 HSSFCell celSum1 = row1.CreateCell(3); 7 HSSFCell celSum2 = row1.CreateCell(4); 8 HSSFCell celSum3 = row1.CreateCell(5); 9 cel1.SetCellValue(1); 10 cel2.SetCellValue(2); 11 cel3.SetCellValue(3); 12 celSum2.SetCellFormula("sum(A1,C1)");
當然,把每一個單元格作為Sum函數的參數很容易理解,但如果要求和的單元格很多,那么公式就會很長,既不方便閱讀也不方便書寫。所以Excel提供了另外一種多個單元格求和的寫法:
如上圖中的“Sum(A1:C1)”表示求從A1到C1所有單元格的和,相當於A1+B1+C1。 對應的代碼為:
celSum1.SetCellFormula("sum(A1:C1)");
最后,還有一種求和的方法。就是先定義一個區域,如”range1”,然后再設置Sum(range1),此時將計算區域中所有單元格的和。 定義區域的代碼為:
HSSFName range = hssfworkbook.CreateName(); range.Reference = "Sheet1!$A1:$C1"; range.NameName = "range1";
執行此代碼后的Excel文件將在的公式菜單下的名稱管理器(Excel2007的菜單路徑,2003稍有不同)中看到如下區域定義: 給單元格F1加上公式:
celSum3.SetCellFormula("sum(range1)");
生成的Excel如下圖所示:
Excel中有非常豐富的日期處理函數,在NPOI中同樣得到了很好的支持。如下圖: 對應的與前面的基本公式設置類似:
1 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 2 HSSFRow row1 = sheet1.CreateRow(0); 3 HSSFRow row2 = sheet1.CreateRow(1); 4 row1.CreateCell(0).SetCellValue("姓名"); 5 row1.CreateCell(1).SetCellValue("參加工作時間"); 6 row1.CreateCell(2).SetCellValue("當前日期"); 7 row1.CreateCell(3).SetCellValue("工作年限"); 8 HSSFCell cel1 = row2.CreateCell(0); 9 HSSFCell cel2 = row2.CreateCell(1); 10 HSSFCell cel3 = row2.CreateCell(2); 11 HSSFCell cel4 = row2.CreateCell(3); 12 cel1.SetCellValue("aTao.Xiang"); 13 cel2.SetCellValue(new DateTime(2004, 7, 1)); 14 cel3.SetCellFormula("TODAY()"); 15 cel4.SetCellFormula("CONCATENATE(DATEDIF(B2,TODAY(),\"y\"),\"年\",DATEDIF(B2,TODAY(),\"ym\"),\"個月\")"); 16 //在poi中日期是以double類型表示的,所以要格式化 HSSFCellStyle cellStyle = hssfworkbook.CreateCellStyle(); HSSFDataFormat format = hssfworkbook.CreateDataFormat(); cellStyle.DataFormat = format.GetFormat("yyyy-m-d"); 17 cel2.CellStyle = cellStyle; 18 19 cel3.CellStyle = cellStyle;
下面對上例中用到的幾個主要函數作一些說明: TODAY():取得當前日期; DATEDIF(B2,TODAY(),"y"):取得B2單元格的日期與前日期以年為單位的時間間隔。(“Y”:表示以年為單位,”m”表示以月為單位;”d”表示以天為單位); CONCATENATE(str1,str2,...):連接字符串。
另外附上Excel中常用的日期函數列表,只需要將此句代碼作適當修改即可:
cel4.SetCellFormula("CONCATENATE(DATEDIF(B2,TODAY(),\"y\"),\"年\",DATEDIF(B2,TODAY(),\"ym\"),\"個月\")");
| 函數名 |
函數說明 |
語法 |
| DATE |
返回代表特定日期的系列數。 |
DATE(year,month,day) |
| DATEDIF |
計算兩個日期之間的天數、月數或年數。 |
DATEDIF(start_date,end_date,unit) |
| DATEVALUE |
函數 DATEVALUE 的主要功能是將以文字表示的日期轉換成一個系列數。 |
DATEVALUE(date_text) |
| DAY |
返回以系列數表示的某日期的天數,用整數 1 到 31 表示。 |
DAY(serial_number) |
| DAYS360 |
按照一年 360 天的算法(每個月以 30 天計,一年共計 12 個月),返回兩日期間相差的天數。 |
DAYS360(start_date,end_date,method) |
| EDATE |
返回指定日期 (start_date) 之前或之后指定月份數的日期系列數。使用函數 EDATE 可以計算與發行日處於一月中同一天的到期日的日期。 |
EDATE(start_date,months) |
| EOMONTH |
返回 start-date 之前或之后指定月份中最后一天的系列數。用函數 EOMONTH 可計算特定月份中最后一天的時間系列數,用於證券的到期日等計算。 |
EOMONTH(start_date,months) |
| HOUR |
返回時間值的小時數。即一個介於 0 (12:00 A.M.) 到 23 (11:00 P.M.) 之間的整數。 |
HOUR(serial_number) |
| MINUTE |
返回時間值中的分鍾。即一個介於 0 到 59 之間的整數。 |
MINUTE(serial_number) |
| MONTH |
返回以系列數表示的日期中的月份。月份是介於 1(一月)和 12(十二月)之間的整數。 |
MONTH(serial_number) |
| NETWORKDAYS |
返回參數 start-data 和 end-data 之間完整的工作日數值。工作日不包括周末和專門指定的假期 |
NETWORKDAYS(start_date,end_date,holidays) |
| NOW |
返回當前日期和時間所對應的系列數。 |
NOW( ) |
| SECOND |
返回時間值的秒數。返回的秒數為 0 至 59 之間的整數。 |
SECOND(serial_number) |
| TIME |
返回某一特定時間的小數值,函數 TIME 返回的小數值為從 0 到 0.99999999 之間的數值,代表從 0:00:00 (12:00:00 A.M) 到 23:59:59 (11:59:59 P.M) 之間的時間。 |
TIME(hour,minute,second) |
| TIMEVALUE |
返回由文本串所代表的時間的小數值。該小數值為從 0 到 0.999999999 的數值,代表從 0:00:00 (12:00:00 AM) 到 23:59:59 (11:59:59 PM) 之間的時間。 |
TIMEVALUE(time_text) |
| TODAY |
返回當前日期的系列數,系列數是 Microsoft Excel 用於日期和時間計算的日期-時間代碼。 |
TODAY( ) |
| WEEKDAY |
返回某日期為星期幾。默認情況下,其值為 1(星期天)到 7(星期六)之間的整數。 |
WEEKDAY(serial_number,return_type) |
| WEEKNUM |
返回一個數字,該數字代表一年中的第幾周。 |
WEEKNUM(serial_num,return_type) |
| WORKDAY |
返回某日期(起始日期)之前或之后相隔指定工作日的某一日期的日期值。工作日不包括周末和專門指定的假日。 |
WORKDAY(start_date,days,holidays) |
| YEAR |
返回某日期的年份。返回值為 1900 到 9999 之間的整數。 |
YEAR(serial_number) |
| YEARFRAC |
返回 start_date 和 end_date 之間的天數占全年天數的百分比。 |
YEARFRAC(start_date,end_date,basis) |
這一節我們開始學習Excel另一類非常常見的函數—字符串函數。 在Excel中提供了非常豐富的字符串函數,在NPOI中同樣得到了很好的支持。 一、大小寫轉換類函數 LOWER(String):將一個文字串中的所有大寫字母轉換為小寫字母。 UPPER(String):將文本轉換成大寫形式。 PROPER(String):將文字串的首字母及任何非字母字符之后的首字母轉換成大寫。將其余的字母轉換成小寫。 對應的C#代碼與前幾節講的設置公式的代碼類似:
1 HSSFRow row1 = sheet1.CreateRow(0); 2 row1.CreateCell(0).SetCellValue("待操作字符串"); 3 row1.CreateCell(1).SetCellValue("操作函數"); 4 row1.CreateCell(2).SetCellValue("操作結果"); 5 HSSFRow row2 = sheet1.CreateRow(1); 6 row2.CreateCell(0).SetCellValue("This is a NPOI example!"); 7 row2.CreateCell(1).SetCellValue("LOWER(A2)"); //將此句中的“LOWER(A2)改 8 //成”UPPER (A2)、PROPER (A2)可以看到不同效果。 9 10 row2.CreateCell(2).SetCellFormula("LOWER(A2)");
二、取出字符串中的部分字符 LEFT(text,num_chars):LEFT(text,num_chars)其中Text是包含要提取字符的文本串。Num_chars指定要由 LEFT 所提取的字符數。
MID(text,start_num,num_chars):MID(text,start_num,num_chars)其中Text是包含要提取字符的文本串。Start_num是文本中要提取的第一個字符的位置,num_chars表示要提取的字符的數。
RIGHT(text,num_chars):RIGHT(text,num_chars)其中Text是包含要提取字符的文本串。Num_chars指定希望 RIGHT 提取的字符數。 代碼與上面類似,就不寫了。
三、去除字符串的空白 TRIM(text):其中Text為需要清除其中空格的文本。需要注意的是,與C#中的Trim不同,Excel中的Trim函數不僅會刪除字符串頭尾的字符,字符串中的多余字符也會刪除,單詞之間只會保留一個空格。
四、字符串的比較 EXACT(text1,text2):比較兩個字符串是否相等,區分大小寫。
執行效果如下:
在此只簡單的講了一下常用的函數,Excel中還有很多的字符串函數,在此附上,讀者可以一個一個去測試。
| 函數名 |
函數說明 |
語法 |
| ASC |
將字符串中的全角(雙字節)英文字母更改為半角(單字節)字符。 |
ASC(text) |
| CHAR |
返回對應於數字代碼的字符,函數 CHAR 可將其他類型計算機文件中的代碼轉換為字符。 |
CHAR(number) |
| CLEAN |
刪除文本中不能打印的字符。對從其他應用程序中輸入的字符串使用 CLEAN 函數,將刪除其中含有的當前操作系統無法打印的字符。例如,可以刪除通常出現在數據文件頭部或尾部、無法打印的低級計算機代碼。 |
CLEAN(text) |
| CODE |
返回文字串中第一個字符的數字代碼。返回的代碼對應於計算機當前使用的字符集。 |
CODE(text) |
| CONCATENATE |
將若干文字串合並到一個文字串中。 |
CONCATENATE (text1,text2,...) |
| DOLLAR |
依照貨幣格式將小數四舍五入到指定的位數並轉換成文字。 |
DOLLAR 或 RMB(number,decimals) |
| EXACT |
該函數測試兩個字符串是否完全相同。如果它們完全相同,則返回 TRUE;否則,返回 FALSE。函數 EXACT 能區分大小寫,但忽略格式上的差異。利用函數 EXACT 可以測試輸入文檔內的文字。 |
EXACT(text1,text2) |
| FIND |
FIND 用於查找其他文本串 (within_text) 內的文本串 (find_text),並從 within_text 的首字符開始返回 find_text 的起始位置編號。 |
FIND(find_text,within_text,start_num) |
| FIXED |
按指定的小數位數進行四舍五入,利用句點和逗號,以小數格式對該數設置格式,並以文字串形式返回結果。 |
FIXED(number,decimals,no_commas) |
| JIS |
將字符串中的半角(單字節)英文字母或片假名更改為全角(雙字節)字符。 |
JIS(text) |
| LEFT |
LEFT 基於所指定的字符數返回文本串中的第一個或前幾個字符。 LEFTB 基於所指定的字節數返回文本串中的第一個或前幾個字符。此函數用於雙字節字符。 |
LEFT(text,num_chars) LEFTB(text,num_bytes) |
| LEN |
LEN 返回文本串中的字符數。 LENB 返回文本串中用於代表字符的字節數。此函數用於雙字節字符。 |
LEN(text) LENB(text) |
| LOWER |
將一個文字串中的所有大寫字母轉換為小寫字母。 |
LOWER(text) |
| MID |
MID 返回文本串中從指定位置開始的特定數目的字符,該數目由用戶指定。 MIDB 返回文本串中從指定位置開始的特定數目的字符,該數目由用戶指定。此函數用於雙字節字符。 |
MID(text,start_num,num_chars) MIDB(text,start_num,num_bytes) |
| PHONETIC |
提取文本串中的拼音 (furigana) 字符。 |
PHONETIC(reference) |
| PROPER |
將文字串的首字母及任何非字母字符之后的首字母轉換成大寫。將其余的字母轉換成小寫。 |
PROPER(text) |
| REPLACE |
REPLACE 使用其他文本串並根據所指定的字符數替換某文本串中的部分文本。 REPLACEB 使用其他文本串並根據所指定的字符數替換某文本串中的部分文本。此函數專為雙字節字符使用。 |
REPLACE(old_text,start_num,num_chars,new_text) REPLACEB(old_text,start_num,num_bytes,new_text) |
| REPT |
按照給定的次數重復顯示文本。可以通過函數 REPT 來不斷地重復顯示某一文字串,對單元格進行填充。 |
REPT(text,number_times) |
| RIGHT |
RIGHT 根據所指定的字符數返回文本串中最后一個或多個字符。 RIGHTB 根據所指定的字符數返回文本串中最后一個或多個字符。此函數用於雙字節字符。 |
RIGHT(text,num_chars) RIGHTB(text,num_bytes) |
| SEARCH |
SEARCH 返回從 start_num 開始首次找到特定字符或文本串的位置上特定字符的編號。使用 SEARCH 可確定字符或文本串在其他文本串中的位置,這樣就可使用 MID 或 REPLACE 函數更改文本。 SEARCHB 也可在其他文本串 (within_text) 中查找文本串 (find_text),並返回 find_text 的起始位置編號。此結果是基於每個字符所使用的字節數,並從 start_num 開始的。此函數用於雙字節字符。此外,也可使用 FINDB 在其他文本串中查找文本串。 |
SEARCH(find_text,within_text,start_num) SEARCHB(find_text,within_text,start_num) |
| SUBSTITUTE |
在文字串中用 new_text 替代 old_text。如果需要在某一文字串中替換指定的文本,請使用函數 SUBSTITUTE;如果需要在某一文字串中替換指定位置處的任意文本,請使用函數 REPLACE。 |
SUBSTITUTE(text,old_text,new_text,instance_num) |
| T |
將數值轉換成文本。 |
T(value) |
| TEXT |
將一數值轉換為按指定數字格式表示的文本。 |
TEXT(value,format_text) |
| TRIM |
除了單詞之間的單個空格外,清除文本中所有的空格。在從其他應用程序中獲取帶有不規則空格的文本時,可以使用函數 TRIM。 |
TRIM(text) |
| UPPER |
將文本轉換成大寫形式。 |
UPPER(text) |
| VALUE |
將代表數字的文字串轉換成數字。 |
VALUE(text) |
| WIDECHAR |
將單字節字符轉換為雙字節字符。 |
WIDECHAR(text) |
| YEN |
使用 ¥(日圓)貨幣格式將數字轉換成文本,並對指定位置后的數字四舍五入。 |
YEN(number,decimals) |
在Excel中,IF(logical_test,value_if_true,value_if_false)用來用作邏輯判斷。其中Logical_test表示計算結果為 TRUE 或 FALSE 的任意值或表達式 ; value_if_true表示當表達式Logical_test的值為TRUE時的返回值;value_if_false表示當表達式Logical_test的值為FALSE時的返回值。同樣在NPOI中也可以利用這個表達式進行各種邏輯運算。如下代碼分別設置了B2和D2單元格的用於邏輯判斷的公式。
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFRow row1 = sheet1.CreateRow(0); row1.CreateCell(0).SetCellValue("姓名"); row1.CreateCell(1).SetCellValue("身份證號"); row1.CreateCell(2).SetCellValue("性別"); row1.CreateCell(3).SetCellValue("語文"); row1.CreateCell(4).SetCellValue("是否合格"); HSSFRow row2 = sheet1.CreateRow(1); row2.CreateCell(0).SetCellValue("令狐沖"); row2.CreateCell(1).SetCellValue("420821198808101014"); row2.CreateCell(2).SetCellFormula("IF(MOD(MID(B2,18,1),2)=0,\"男\",\"女\")"); row2.CreateCell(3).SetCellValue(85); row2.CreateCell(4).SetCellFormula("IF(D2>60,IF(D2>90,\"優秀\",\"合格\"),\"不合格\")");
其中最關鍵的兩句執行結果如下:
row2.CreateCell(2).SetCellFormula("IF(MOD(MID(B2,18,1),2)=0,\"男\",\"女\")");
row2.CreateCell(4).SetCellFormula("IF(D2>60,IF(D2>90,\"優秀\",\"合格\"),\"不合格\")");
下面分別對這幾個函數作一些說明: MOD(MID(B2,18,1),2):我們知道18位身份證號的第18位表示性別,偶數為男性,奇數為女性,所以用了MID(B2,18,1)取第18位數字(與C#中一般從0計數不同,第二個參數是從1算起,有關MID函數的更多信息,請參見字符串函數),用MOD取余函數判斷奇偶。在Excel中對數據類型的控制沒有C#中那么嚴格,如此例中我截取出來的是字符串,但當我做取余運算時Excel會自動轉換。 IF(D2>60,IF(D2>90,"優秀","合格"),"不合格"):這是IF的嵌套使用,表示90分以上為優秀,60分以上為合格,否則為不合格。
一、COUNTIF 這一節,我們一起來學習Excel中另一個常用的函數--COUNTIF函數,看函數名就知道這是一個用來在做滿足某條件的計數的函數。先來看一看它的語法:COUNTIF(range,criteria),參數說明如下:
| Range |
需要進行讀數的計數 |
| Criteria |
條件表達式,只有當滿足此條件時才進行計數 |
接下來看一個例子,代碼如下:
1 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 2 HSSFRow row1 = sheet1.CreateRow(0); row1.CreateCell(0).SetCellValue("姓名"); row1.CreateCell(1).SetCellValue("成績"); 3 HSSFRow row2 = sheet1.CreateRow(1); row2.CreateCell(0).SetCellValue("令狐沖"); row2.CreateCell(1).SetCellValue(85); 4 HSSFRow row3 = sheet1.CreateRow(2); row3.CreateCell(0).SetCellValue("任盈盈"); row3.CreateCell(1).SetCellValue(90); 5 HSSFRow row4 = sheet1.CreateRow(3); row4.CreateCell(0).SetCellValue("任我行"); row4.CreateCell(1).SetCellValue(70); 6 HSSFRow row5 = sheet1.CreateRow(4); row5.CreateCell(0).SetCellValue("左冷嬋"); row5.CreateCell(1).SetCellValue(45); 7 HSSFRow row6 = sheet1.CreateRow(5); row6.CreateCell(0).SetCellValue("岳不群"); row6.CreateCell(1).SetCellValue(50); 8 HSSFRow row7 = sheet1.CreateRow(6); row7.CreateCell(0).SetCellValue("合格人數:"); row7.CreateCell(1).SetCellFormula("COUNTIF(B2:B6,\">60\")");
執行結果如下:
我們可以看到,CountIf函數成功的統計出了區域“B2:B6”中成績合格的人數(這里定義成績大於60為合格)。
二、SUMIF
接下來,順便談談另一個與CountIF類似的函數—SumIf函數。此函數用於統計某區域內滿足某條件的值的求和(CountIf是計數)。與CountIF不同,SumIF有三個參數,語法為 SumIF(criteria_range, criteria,sum_range),各參數的說明如下:
| criteria_range |
條件測試區域,第二個參數Criteria中的條件將與此區域中的值進行比較 |
| criteria |
條件測試值,滿足條件的對應的sum_range項將進行求和計算 |
| sum_range |
匯總數據所在區域,求和時會排除掉不滿足Criteria條件的對應的項 |
我們還是以例子來加以說明:
1 Code HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 2 HSSFRow row1 = sheet1.CreateRow(0); row1.CreateCell(0).SetCellValue("姓名"); row1.CreateCell(1).SetCellValue("月份"); row1.CreateCell(2).SetCellValue("銷售額"); 3 HSSFRow row2 = sheet1.CreateRow(1); row2.CreateCell(0).SetCellValue("令狐沖"); row2.CreateCell(1).SetCellValue("一月"); row2.CreateCell(2).SetCellValue(1000); 4 HSSFRow row3 = sheet1.CreateRow(2); row3.CreateCell(0).SetCellValue("任盈盈"); row3.CreateCell(1).SetCellValue("一月"); row3.CreateCell(2).SetCellValue(900); 5 HSSFRow row4 = sheet1.CreateRow(3); row4.CreateCell(0).SetCellValue("令狐沖"); row4.CreateCell(1).SetCellValue("二月"); row4.CreateCell(2).SetCellValue(2000); 6 HSSFRow row5 = sheet1.CreateRow(4); row5.CreateCell(0).SetCellValue("任盈盈"); row5.CreateCell(1).SetCellValue("二月"); row5.CreateCell(2).SetCellValue(1000); 7 HSSFRow row6 = sheet1.CreateRow(5); row6.CreateCell(0).SetCellValue("令狐沖"); row6.CreateCell(1).SetCellValue("三月"); row6.CreateCell(2).SetCellValue(3000); 8 HSSFRow row7 = sheet1.CreateRow(6); row7.CreateCell(0).SetCellValue("任盈盈"); row7.CreateCell(1).SetCellValue("三月"); row7.CreateCell(2).SetCellValue(1200); 9 HSSFRow row8 = sheet1.CreateRow(7); row8.CreateCell(0).SetCellValue("令狐沖一季度銷售額:"); row8.CreateCell(2).SetCellFormula("SUMIF(A2:A7,\"=令狐沖\",C2:C7)"); 10 HSSFRow row9 = sheet1.CreateRow(8); row9.CreateCell(0).SetCellValue("任盈盈一季度銷售額:"); row9.CreateCell(2).SetCellFormula("SUMIF(A2:A7,\"=任盈盈\",C2:C7)");
執行結果如下:
如上圖,SUMIF統計出了不同人一季度的銷售額。
今天,我們一起學習Excel中的查詢函數--LOOKUP。其基本語法形式為LOOKUP(lookup_value,lookup_vector,result_vector)。還是以例子加以說明更容易理解:
1 Code HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFRow row1 = sheet1.CreateRow(0); row1.CreateCell(0).SetCellValue("收入最低"); row1.CreateCell(1).SetCellValue("收入最高"); row1.CreateCell(2).SetCellValue("稅率"); 2 HSSFRow row2 = sheet1.CreateRow(1); row2.CreateCell(0).SetCellValue(0); row2.CreateCell(1).SetCellValue(3000); row2.CreateCell(2).SetCellValue(0.1); 3 HSSFRow row3 = sheet1.CreateRow(2); row3.CreateCell(0).SetCellValue(3001); row3.CreateCell(1).SetCellValue(10000); row3.CreateCell(2).SetCellValue(0.2); 4 HSSFRow row4 = sheet1.CreateRow(3); row4.CreateCell(0).SetCellValue(10001); row4.CreateCell(1).SetCellValue(20000); row4.CreateCell(2).SetCellValue(0.3); 5 HSSFRow row5 = sheet1.CreateRow(4); row5.CreateCell(0).SetCellValue(20001); row5.CreateCell(1).SetCellValue(50000); row5.CreateCell(2).SetCellValue(0.4); 6 HSSFRow row6 = sheet1.CreateRow(5); row6.CreateCell(0).SetCellValue(50001); row6.CreateCell(2).SetCellValue(0.5); 7 HSSFRow row8 = sheet1.CreateRow(7); row8.CreateCell(0).SetCellValue("收入"); row8.CreateCell(1).SetCellValue("稅率"); 8 HSSFRow row9 = sheet1.CreateRow(8); row9.CreateCell(0).SetCellValue(7800); row9.CreateCell(1).SetCellFormula("LOOKUP(A9,$A$2:$A$6,$C$2:$C$6)");
這是一個根據工資查詢相應稅率的例子。我們首先創建了不同工資區間對應稅率的字典,然后根據具體的工資在字典中找出對應的稅率。執行后生成的Excel如下: 下面對各參數加以說明: 第一個參數:需要查找的內容,本例中指向A9單元格,也就是7800; 第二個參數:比較對象區域,本例中的工資需要與$A$2:$A$6中的各單元格中的值進行比較;第三個參數:查找結果區域,如果匹配到會將此區域中對應的數據返回。如本例中返回$C$2:$C$6中對應的值。 可能有人會問,字典中沒有7800對應的稅率啊,那么Excel中怎么匹配的呢?答案是模糊匹配,並且LOOKUP函數只支持模糊匹配。Excel會在$A$2:$A$6中找小於7800的最大值,也就是A3對應的3001,然后將對應的$C$2:$C$6區域中的C3中的值返回,這就是最終結果0.2的由來。這下明白了吧:)
VLOOKUP 另外,LOOKUP函數還有一位大哥--VLOOKUP。兩兄弟有很多相似之處,但大哥本領更大。Vlookup用對比數與一個“表”進行對比,而不是Lookup函數的某1列或1行,並且Vlookup可以選擇采用精確查詢或是模糊查詢方式,而Lookup只有模糊查詢。 將上例中設置公式的代碼換成:
row9.CreateCell(1).SetCellFormula("VLOOKUP(A9,$A$2:$C$6,3,TRUE)");
執行后生成的Excel樣式如下:
第一個參數:需要查找的內容,這里是A9單元格; 第二個參數:需要比較的表,這里是$A$2:$C$6,注意VLOOKUP匹配時只與表中的第一列進行匹配。 第三個參數:匹配結果對應的列序號。這里要對應的是稅率列,所以為3。 第四個參數:指明是否模糊匹配。例子中的TRUE表示模糊匹配,與上例中一樣。匹配到的是第三行。如果將此參數改為FALSE,因為在表中的第1列中找不到7800,所以會報“#N/A”的計算錯誤。
另外,還有與VLOKUP類似的HLOOKUP。不同的是VLOOKUP用於在表格或數值數組的首列查找指定的數值,並由此返回表格或數組當前行中指定列處的數值。而HLOOKUP用於在表格或數值數組的首行查找指定的數值,並由此返回表格或數組當前列中指定行處的數值。讀者可以自已去嘗試。
我們知道,在大多數編程語言中都有隨機數函數。在Excel中,同樣存在着這樣一個函數—RAND()函數,用於生成隨機數。先來看一個最簡單的例子:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); sheet1.CreateRow(0).CreateCell(0).SetCellFormula("RAND()");
RAND()函數將返回一個0-1之間的隨機數,執行后生成的Excel文件如下:
這只是最簡單直接的RAND()函數的應用,只要我們稍加修改,就可以作出很多種變換。如 取0-100之前的隨機整數,可設置公式為:
sheet1.CreateRow(0).CreateCell(0).SetCellFormula("int(RAND()*100)");
取10-20之間的隨機實數,可設置公式為:
sheet1.CreateRow(0).CreateCell(0).SetCellFormula("rand()*(20-10)+10");
隨機小寫字母:
sheet1.CreateRow(0).CreateCell(0).SetCellFormula("CHAR(INT(RAND()*26)+97)");
隨機大寫字母:
sheet1.CreateRow(0).CreateCell(0).SetCellFormula("CHAR(INT(RAND()*26)+65)")
隨機大小寫字母:
sheet1.CreateRow(0).CreateCell(0).SetCellFormula("CHAR(INT(RAND()*26)+if(INT(RAND()*2)=0,65,97))");
上面幾例中除了用到RAND函數以外,還用到了CHAR函數用來將ASCII碼換為字母,INT函數用來取整。值得注意的是INT函數不會四舍五入,無論小數點后是多少都會被舍去。 這里只是RAND函數的幾個簡單應用,還有很多隨機數的例子都可以根據這些,再結合不同的其它函數引申出來。
前面我們學習了通過NPOI向Excel中設置公式,那么有些讀者可能會問:“NPOI能不能獲取公式的返回值呢?”,答案是可以! 一、獲取模板文件中公式的返回值如在D盤中有一個名為text.xls的Excel文件,其內容如下:
注意C1單元格中設置的是公式“$A1*$B1”,而不是值“12”。利用NPOI,只需要寫簡單的幾句代碼就可以取得此公式的返回值:
HSSFWorkbook wb = new HSSFWorkbook(new FileStream("d:/test.xls",FileMode.Open)); HSSFCell cell = wb.GetSheet("Sheet1").GetRow(0).GetCell(2); System.Console.WriteLine(cell.NumericCellValue);
輸出結果為:
可見NPOI成功的“解析”了此.xls文件中的公式。注意NumericCellValue屬性會自動根據單元格的類型處理,如果為空將返0,如果為數值將返回數值,如果為公式將返回公式計算后的結果。單元格的類型可以通過CellType屬性獲取。
二、獲取NPOI生成的Excel文件中公式的返回值 上例中是從一個已經存在的Excel文件中獲取公式的返回值,那么如果Excel文件是通過NPOI創建的,直接用上面的方法獲取,可能得不到想要的結果。如:
1 HSSFWorkbook hssfworkbook = new HSSFWorkbook(); 2 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 3 HSSFRow row = sheet1.CreateRow(0); 4 row.CreateCell(0).SetCellValue(3); 5 row.CreateCell(1).SetCellValue(4); 6 HSSFCell cell = row.CreateCell(2); 7 8 cell.SetCellFormula("$A1+$B1"); 9 System.Console.WriteLine(cell.NumericCellValue);
執行上面代碼,將輸出結果“0”,而不是我們想要的結果“7”。那么將如何解決呢?這時要用到HSSFFormulaEvaluator類。在第8行后加上這兩句就可以了:
HSSFFormulaEvaluator e = new HSSFFormulaEvaluator(hssfworkbook); cell = e.EvaluateInCell(cell);
運行結果如下:
之所有說NPOI強大,是因為常用的Excel操作她都可以通過編程的方式完成。這節開始,我們開始學習NPOI的畫圖功能。先從最簡單的開始,畫一條直線:
對應的代碼為:
1 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 2 HSSFPatriarch patriarch = sheet1.CreateDrawingPatriarch(); 3 HSSFClientAnchor a1 = new HSSFClientAnchor(255, 125, 1023, 150, 0, 0,2, 2); 4 HSSFSimpleShape line1 = patriarch.CreateSimpleShape(a1); 5 line1.ShapeType = HSSFSimpleShape.OBJECT_TYPE_LINE; 6 line1.LineStyle = HSSFShape.LINESTYLE_SOLID; //在NPOI中線的寬度12700表示1pt,所以這里是0.5pt粗的線條。 line1.LineWidth = 6350;
通常,利用NPOI畫圖主要有以下幾個步驟: 1. 創建一個Patriarch; 2. 創建一個Anchor,以確定圖形的位置; 3. 調用Patriarch創建圖形; 4. 設置圖形類型(直線,矩形,圓形等)及樣式(顏色,粗細等)。
關於HSSFClientAnchor(dx1,dy1,dx2,dy2,col1,row1,col2,row2)的參數,有必要在這里說明一下: dx1:起始單元格的x偏移量,如例子中的255表示直線起始位置距A1單元格左側的距離; dy1:起始單元格的y偏移量,如例子中的125表示直線起始位置距A1單元格上側的距離; dx2:終止單元格的x偏移量,如例子中的1023表示直線起始位置距C3單元格左側的距離; dy2:終止單元格的y偏移量,如例子中的150表示直線起始位置距C3單元格上側的距離; col1:起始單元格列序號,從0開始計算; row1:起始單元格行序號,從0開始計算,如例子中col1=0,row1=0就表示起始單元格為A1; col2:終止單元格列序號,從0開始計算; row2:終止單元格行序號,從0開始計算,如例子中col2=2,row2=2就表示起始單元格為C3;
最后,關於LineStyle屬性,有如下一些可選值,對應的效果分別如圖所示:
上一節我們講了NPOI中畫圖的基本步驟: 1. 創建一個Patriarch; 2. 創建一個Anchor,以確定圖形的位置; 3. 調用Patriarch創建圖形; 4. 設置圖形類型(直線,矩形,圓形等)及樣式(顏色,粗細等)。
這一節我們將按照這個步驟創建一個矩形。廢話少說,上代碼:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFPatriarch patriarch = sheet1.CreateDrawingPatriarch(); HSSFClientAnchor a1 = new HSSFClientAnchor(255, 125, 1023, 150, 0, 0, 2, 2); HSSFSimpleShape rec1 = patriarch.CreateSimpleShape(a1); //此處設置圖形類型為矩形 rec1.ShapeType = HSSFSimpleShape.OBJECT_TYPE_RECTANGLE; //設置填充色 rec1.SetFillColor(125, 125, 125); //設置邊框樣式 rec1.LineStyle = HSSFShape.LINESTYLE_DASHGEL; //設置邊框寬度 rec1.LineWidth = 25400; //設置邊框顏色 rec1.SetLineStyleColor(100, 0, 100);
代碼執行效果: 其中SetFillColor和SetLineStyleColor函數的三個參數分別是RGB三色值,具體表示什么顏色,找個Photoshop試試:) 關於HSSFClientAnchor參數說明、邊框樣式,邊框寬度的說明可以參見前一篇博文: http://www.cnblogs.com/atao/archive/2009/09/13/1565645.html
前面我們學習了NPOI中的畫簡單直線和矩形的功能,今天我們一起學習一下它支持的另一種簡單圖形--圓形。同樣,按照前面所講的繪圖“四步曲”: 1. 創建一個Patriarch; 2. 創建一個Anchor,以確定圖形的位置; 3. 調用Patriarch創建圖形; 4. 設置圖形類型(直線,矩形,圓形等)及樣式(顏色,粗細等)。 還是以例子加以說明:
1 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); 2 HSSFPatriarch patriarch = sheet1.CreateDrawingPatriarch(); 3 HSSFClientAnchor a1 = new HSSFClientAnchor(0, 0, 1023, 0, 0, 0, 1, 3); 4 HSSFSimpleShape rec1 = patriarch.CreateSimpleShape(a1); 5 rec1.ShapeType = HSSFSimpleShape.OBJECT_TYPE_OVAL; 6 rec1.SetFillColor(125, 125, 125); 7 rec1.LineStyle = HSSFShape.LINESTYLE_DASHGEL; 8 rec1.LineWidth = 12700; 9 rec1.SetLineStyleColor(100, 0, 100); WriteToFile();
這里rec1.ShapeType = HSSFSimpleShape.OBJECT_TYPE_OVAL;表示圖形為橢圓。適當調整HSSFClientAnchor的各參數可以得到圓形。 關於HSSFClientAnchor構造函數和邊框、填充色等前兩節都有介紹,這里不再重述。詳情情見:畫矩形 和畫線。
上面代碼執行生成的Excel如下:
在NPOI中,本身沒有畫Grid的方法。但我們知道Grid其實就是由橫線和豎線構成的,所在我們可以通過畫線的方式來模擬畫Grid。
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1");
HSSFRow row = sheet1.CreateRow(2); row.CreateCell(1); row.HeightInPoints = 240; sheet1.SetColumnWidth(2, 9000); int linesCount = 20;
HSSFPatriarch patriarch = sheet1.CreateDrawingPatriarch(); //因為HSSFClientAnchor中dx只能在0-1023之間,dy只能在0-255之間,所以這里采用比例的方式 double xRatio = 1023.0 / (linesCount*10); double yRatio = 255.0 / (linesCount*10);
//畫豎線 int x1 = 0; int y1 = 0; int x2 = 0; int y2 = 200; for (int i = 0; i < linesCount; i++) { HSSFClientAnchor a2 = new HSSFClientAnchor(); a2.SetAnchor((short)2, 2, (int)(x1 * xRatio), (int)(y1 * yRatio), (short)2, 2, (int)(x2 * xRatio), (int)(y2 * yRatio)); HSSFSimpleShape shape2 = patriarch.CreateSimpleShape(a2); shape2.ShapeType = (HSSFSimpleShape.OBJECT_TYPE_LINE);
x1 += 10; x2 += 10; }
//畫橫線 x1 = 0; y1 = 0; x2 = 200; y2 = 0; for (int i = 0; i < linesCount; i++) { HSSFClientAnchor a2 = new HSSFClientAnchor(); a2.SetAnchor((short)2, 2, (int)(x1 * xRatio), (int)(y1 * yRatio), (short)2, 2, (int)(x2 * xRatio), (int)(y2 * yRatio)); HSSFSimpleShape shape2 = patriarch.CreateSimpleShape(a2); shape2.ShapeType = (HSSFSimpleShape.OBJECT_TYPE_LINE);
y1 += 10; y2 += 10; }
請注意HSSFClientAnchor對象中的dx只能取0-1023之間的數,dy只能取0-255之間的數。我們可以理解為是將單元格的寬和高平分成了1023和255份,設置dx和dy時相當於按比例取對應的座標。最終生成的Excel如下:
我們知道,在Excel中是可以插入圖片的。操作菜單是“插入->圖片”,然后選擇要插入圖片,可以很容易地在Excel插入圖片。同樣,在NPOI中,利用代碼也可以實現同樣的效果。在NPOI中插入圖片的方法與畫圖的方法有點類似:
//add picture data to this workbook. byte[] bytes = System.IO.File.ReadAllBytes(@"D:\MyProject\NPOIDemo\ShapeImage\image1.jpg"); int pictureIdx = hssfworkbook.AddPicture(bytes, HSSFWorkbook.PICTURE_TYPE_JPEG);
//create sheet HSSFSheet sheet = hssfworkbook.CreateSheet("Sheet1");
// Create the drawing patriarch. This is the top level container for all shapes. HSSFPatriarch patriarch = sheet.CreateDrawingPatriarch();
//add a picture HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 0, 0, 0, 1, 3); HSSFPicture pict = patriarch.CreatePicture(anchor, pictureIdx);
與畫簡單圖形不同的是,首先要將圖片讀入到byte數組,然后添加到workbook中;最后調用的是patriarch.CreatePicture(anchor, pictureIdx)方法顯示圖片,而不是patriarch.CreateSimpleShape(anchor)方法。上面這段代碼執行后生成的Excel文件樣式如下:
我們發現,插入的圖片被拉伸填充在HSSFClientAnchor指定的區域。有時可能我們並不需要拉伸的效果,怎么辦呢?很簡單,在最后加上這樣一句用來自動調節圖片大小:
pict.Resize();
添加代碼后再執行上述代碼,生成的Excel樣式如下:
圖片已經自動伸縮到原始大小了。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
打印設置主要包括方向設置、縮放、紙張設置、頁邊距等。NPOI 1.2支持大部分打印屬性,能夠讓你輕松滿足客戶的打印需要。
首先是方向設置,Excel支持兩種頁面方向,即縱向和橫向。
在NPOI中如何設置呢?你可以通過HSSFSheet.PrintSetup.Landscape來設置,Landscape是布爾類型的,在英語中是橫向的意思。如果Landscape等於true,則表示頁面方向為橫向;否則為縱向。
接着是縮放設置,
這里的縮放比例對應於HSSFSheet.PrintSetup.Scale,而頁寬和頁高分別對應於HSSFSheet.PrintSetup.FitWidth和HSSFSheet.PrintSetup.FitHeight。要注意的是,這里的PrintSetup.Scale應該被設置為0-100之間的值,而不是小數。
接下來就是紙張設置了,對應於HSSFSheet.PrintSetup.PaperSize,但這里的PaperSize並不是隨便設置的,而是由一些固定的值決定的,具體的值與對應的紙張如下表所示:
| 值 |
紙張 |
| 1 |
US Letter 8 1/2 x 11 in |
| 2 |
US Letter Small 8 1/2 x 11 in |
| 3 |
US Tabloid 11 x 17 in |
| 4 |
US Ledger 17 x 11 in |
| 5 |
US Legal 8 1/2 x 14 in |
| 6 |
US Statement 5 1/2 x 8 1/2 in |
| 7 |
US Executive 7 1/4 x 10 1/2 in |
| 8 |
A3 297 x 420 mm |
| 9 |
A4 210 x 297 mm |
| 10 |
A4 Small 210 x 297 mm |
| 11 |
A5 148 x 210 mm |
| 12 |
B4 (JIS) 250 x 354 |
| 13 |
B5 (JIS) 182 x 257 mm |
| 14 |
Folio 8 1/2 x 13 in |
| 15 |
Quarto 215 x 275 mm |
| 16 |
10 x 14 in |
| 17 |
11 x 17 in |
| 18 |
US Note 8 1/2 x 11 in |
| 19 |
US Envelope #9 3 7/8 x 8 7/8 |
| 20 |
US Envelope #10 4 1/8 x 9 1/2 |
| 21 |
US Envelope #11 4 1/2 x 10 3/8 |
| 22 |
US Envelope #12 4 \276 x 11 |
| 23 |
US Envelope #14 5 x 11 1/2 |
| 24 |
C size sheet |
| 25 |
D size sheet |
| 26 |
E size sheet |
| 27 |
Envelope DL 110 x 220mm |
| 28 |
Envelope C5 162 x 229 mm |
| 29 |
Envelope C3 324 x 458 mm |
| 30 |
Envelope C4 229 x 324 mm |
| 31 |
Envelope C6 114 x 162 mm |
| 32 |
Envelope C65 114 x 229 mm |
| 33 |
Envelope B4 250 x 353 mm |
| 34 |
Envelope B5 176 x 250 mm |
| 35 |
Envelope B6 176 x 125 mm |
| 36 |
Envelope 110 x 230 mm |
| 37 |
US Envelope Monarch 3.875 x 7.5 in |
| 38 |
6 3/4 US Envelope 3 5/8 x 6 1/2 in |
| 39 |
US Std Fanfold 14 7/8 x 11 in |
| 40 |
German Std Fanfold 8 1/2 x 12 in |
| 41 |
German Legal Fanfold 8 1/2 x 13 in |
| 42 |
B4 (ISO) 250 x 353 mm |
| 43 |
Japanese Postcard 100 x 148 mm |
| 44 |
9 x 11 in |
| 45 |
10 x 11 in |
| 46 |
15 x 11 in |
| 47 |
Envelope Invite 220 x 220 mm |
| 48 |
RESERVED--DO NOT USE |
| 49 |
RESERVED--DO NOT USE |
| 50 |
US Letter Extra 9 \275 x 12 in |
| 51 |
US Legal Extra 9 \275 x 15 in |
| 52 |
US Tabloid Extra 11.69 x 18 in |
| 53 |
A4 Extra 9.27 x 12.69 in |
| 54 |
Letter Transverse 8 \275 x 11 in |
| 55 |
A4 Transverse 210 x 297 mm |
| 56 |
Letter Extra Transverse 9\275 x 12 in |
| 57 |
SuperA/SuperA/A4 227 x 356 mm |
| 58 |
SuperB/SuperB/A3 305 x 487 mm |
| 59 |
US Letter Plus 8.5 x 12.69 in |
| 60 |
A4 Plus 210 x 330 mm |
| 61 |
A5 Transverse 148 x 210 mm |
| 62 |
B5 (JIS) Transverse 182 x 257 mm |
| 63 |
A3 Extra 322 x 445 mm |
| 64 |
A5 Extra 174 x 235 mm |
| 65 |
B5 (ISO) Extra 201 x 276 mm |
| 66 |
A2 420 x 594 mm |
| 67 |
A3 Transverse 297 x 420 mm |
| 68 |
A3 Extra Transverse 322 x 445 mm |
| 69 |
Japanese Double Postcard 200 x 148 mm |
| 70 |
A6 105 x 148 mm |
| 71 |
Japanese Envelope Kaku #2 |
| 72 |
Japanese Envelope Kaku #3 |
| 73 |
Japanese Envelope Chou #3 |
| 74 |
Japanese Envelope Chou #4 |
| 75 |
Letter Rotated 11 x 8 1/2 11 in |
| 76 |
A3 Rotated 420 x 297 mm |
| 77 |
A4 Rotated 297 x 210 mm |
| 78 |
A5 Rotated 210 x 148 mm |
| 79 |
B4 (JIS) Rotated 364 x 257 mm |
| 80 |
B5 (JIS) Rotated 257 x 182 mm |
| 81 |
Japanese Postcard Rotated 148 x 100 mm |
| 82 |
Double Japanese Postcard Rotated 148 x 200 mm |
| 83 |
A6 Rotated 148 x 105 mm |
| 84 |
Japanese Envelope Kaku #2 Rotated |
| 85 |
Japanese Envelope Kaku #3 Rotated |
| 86 |
Japanese Envelope Chou #3 Rotated |
| 87 |
Japanese Envelope Chou #4 Rotated |
| 88 |
B6 (JIS) 128 x 182 mm |
| 89 |
B6 (JIS) Rotated 182 x 128 mm |
| 90 |
12 x 11 in |
| 91 |
Japanese Envelope You #4 |
| 92 |
Japanese Envelope You #4 Rotated |
| 93 |
PRC 16K 146 x 215 mm |
| 94 |
PRC 32K 97 x 151 mm |
| 95 |
PRC 32K(Big) 97 x 151 mm |
| 96 |
PRC Envelope #1 102 x 165 mm |
| 97 |
PRC Envelope #2 102 x 176 mm |
| 98 |
PRC Envelope #3 125 x 176 mm |
| 99 |
PRC Envelope #4 110 x 208 mm |
| 100 |
PRC Envelope #5 110 x 220 mm |
| 101 |
PRC Envelope #6 120 x 230 mm |
| 102 |
PRC Envelope #7 160 x 230 mm |
| 103 |
PRC Envelope #8 120 x 309 mm |
| 104 |
PRC Envelope #9 229 x 324 mm |
| 105 |
PRC Envelope #10 324 x 458 mm |
| 106 |
PRC 16K Rotated |
| 107 |
PRC 32K Rotated |
| 108 |
PRC 32K(Big) Rotated |
| 109 |
PRC Envelope #1 Rotated 165 x 102 mm |
| 110 |
PRC Envelope #2 Rotated 176 x 102 mm |
| 111 |
PRC Envelope #3 Rotated 176 x 125 mm |
| 112 |
PRC Envelope #4 Rotated 208 x 110 mm |
| 113 |
PRC Envelope #5 Rotated 220 x 110 mm |
| 114 |
PRC Envelope #6 Rotated 230 x 120 mm |
| 115 |
PRC Envelope #7 Rotated 230 x 160 mm |
| 116 |
PRC Envelope #8 Rotated 309 x 120 mm |
| 117 |
PRC Envelope #9 Rotated 324 x 229 mm |
| 118 |
PRC Envelope #10 Rotated 458 x 324 mm |
(此表摘自《Excel Binary File Format (.xls) Structure Specification.pdf》)
HSSFSheet下面定義了一些xxxx_PAPERSIZE的常量,但都是非常常用的紙張大小,如果滿足不了你的需要,可以根據上表自己給PaperSize屬性賦值。所以,如果你要設置紙張大小可以用這樣的代碼:
HSSFSheet.PrintSetup.PaperSize=HSSFSheet.A4_PAPERSIZE;
或
HSSFSheet.PrintSetup.PaperSize=9; (A4 210*297mm)
再下來就是打印的起始頁碼,它對應於HSSFSheet.PrintSetup.PageStart和HSSFSheet.PrintSetup.UsePage,如果UsePage=false,那么就相當於“自動”,這時PageStart不起作用;如果UsePage=true,PageStart才會起作用。所以在設置PageStart之前,必須先把UsePage設置為true。
“打印”欄中的“網格線”設置對應於HSSFSheet.IsPrintGridlines,請注意,這里不是HSSFSheet.PrintSetup下面,所以別搞混了。這里之所以不隸屬於PrintSetup是由底層存儲該信息的record決定的,底層是把IsGridsPrinted放在GridsetRecord里面的,而不是PrintSetupRecord里面的,盡管界面上是放在一起的。另外還有一個HSSFSheet.IsGridsPrinted屬性,這個屬性對應於底層的gridset Record,但這個record是保留的,從微軟的文檔顯示沒有任何意義,所以這個屬性請不要去設置。
“單色打印”則對應於HSSFSheet.PrintSetup.NoColors,這是布爾類型的,值為true時,表示單色打印。
“草稿品質”對應於HSSFSheet.PrintSetup.IsDraft,也是布爾類型的,值為true時,表示用草稿品質打印。
這里的打印順序是由HSSFSheet.PrintSetup.LeftToRight決定的,它是布爾類型的,當為true時,則表示“先行后列”;如果是false,則表示“先列后行”。
在NPOI 1.2中,“行號列標”、“批注”和“錯誤單元格打印為”、“頁邊距”暫不支持,將在以后的版本中支持。
有關打印的范例可以參考 NPOI 1.2正式版 中的 SetPrintSettingsInXls 項目。
在Excel中,可以通過調整右下角的滾動條來調整Sheet的顯示比例。如圖:
在NPOI中,也能通過代碼實現這樣的功能,並且代碼非常簡單:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); sheet1.CreateRow(0).CreateCell(0).SetCellValue("This is a test."); //50% zoom sheet1.SetZoom(1,2);
我們發現,SetZoom有兩個參數。其中第一個參數表示縮放比例的分子,第二個參數表示縮放比例的分母,所以SetZoom(1,2)就表示縮小到1/2,也就是50%。代碼執行后生成的Excel樣式如下:
如果將SetZoom的參數改成(2,1),代碼執行后生成的Excel樣式如下,表示擴大兩倍:
有時,我們可能需要某些單元格只讀,如在做模板時,模板中的數據是不能隨意讓別人改的。在Excel中,可以通過“審閱->保護工作表”來完成,如下圖: 那么,在NPOI中有沒有辦法通過編碼的方式達到這一效果呢?答案是肯定的。
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1");
HSSFRow row1 = sheet1.CreateRow(0); HSSFCell cel1 = row1.CreateCell(0); HSSFCell cel2 = row1.CreateCell(1);
HSSFCellStyle unlocked = hssfworkbook.CreateCellStyle(); unlocked.IsLocked = false;
HSSFCellStyle locked = hssfworkbook.CreateCellStyle(); locked.IsLocked = true;
cel1.SetCellValue("沒被鎖定"); cel1.CellStyle = unlocked;
cel2.SetCellValue("被鎖定"); cel2.CellStyle = locked;
sheet1.ProtectSheet("password");
正如代碼中所看到的,我們通過設置CellStype的ISLocked為True,表示此單元格將被鎖定。相當於在Excel中執行了如下操作:
然后通過ProtectSheet設置密碼。
執行結果如下:
沒被鎖定的列可以任意修改。
被鎖定的列不能修改。
輸入密碼可以解除鎖定。
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
Excel 2007中有一個面板是專門用於設置組合功能的,叫做“分級顯示”面板,如下所示:
可能我們在過去生成Excel文件的時候根本不會用這個功能,也沒辦法用,因為cvs法和html法沒辦法控制這些東西。這里簡單的介紹一下什么叫做組合:
組合分為行組合和列組合,所謂行組合,就是讓n行組合成一個集合,能夠進行展開和合攏操作,在Excel中顯示如下:
圖中左側就是用於控制行組合折疊的圖標,圖中上部就是用於控制列組合的,是不是有點像TreeView中的折疊節點?很多時候由於數據太多,為了讓用戶對於大量數據一目了然,我們可以使用行列組合來解決顯示大綱,這和Visual Studio 里面的region的概念是類似的。
細心的朋友可能已經注意到了,我們其實可以對一行做多次組合操作,這就是分級顯示的概念,圖中就把行2-3分為2個組合,第2行到第4行為一個組合,第2行到第5行一個組合,所以是分兩級。
在NPOI中,要實現分組其實並不難,你只需要調用HSSFSheet.GroupRow和HSSFSheet.GroupColumn這兩個方法就可以了。
首先我們來看HSSFSheet.GroupRow,GroupRow有2個參數,分別是fromRow和toRow,表示起始行號和結束行號,這些行號都是從0開始算起的。
HSSFWorkbook hssfworkbook = new HSSFWorkbook();
HSSFSheet s = hssfworkbook.CreateSheet("Sheet1");
s.GroupRow(1, 3);
上面的代碼把第2行到第4行做了組合。
要組合列,其實代碼很相似,如下所示:
s.GroupColumn(1,3)
上面的代碼把B至D列做了組合。
正如上圖中Excel的“分級顯示”面板所示,有“組合”,也一定有“取消組合”,NPOI中你可以用HSSFSheet.UngroupRow和HSSFSheet.UngroupColumn,參數和GroupXXX是一樣的,如果要取消第2到第4行的組合,就可以用下面的代碼:
s.UngroupColumn(1,3)
相關范例請見NPOI 1.2正式版中的GroupRowAndColumnInXls項目。
在Excel中,有時可能會出現列數太多或是行數太多的情況,這時可以通過鎖定列來凍結部分列,不隨滾動條滑動,方便查看。在Excel中設置凍結列的方法如下:
同樣,利用NPOI,通過代碼也能實現上面的效果:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFRow row1 = sheet1.CreateRow(0); row1.CreateCell(0).SetCellValue("凍結列"); sheet1.CreateFreezePane(1, 0, 1, 0);
代碼執行結果如下:
下面對CreateFreezePane的參數作一下說明: 第一個參數表示要凍結的列數; 第二個參數表示要凍結的行數,這里只凍結列所以為0; 第三個參數表示右邊區域可見的首列序號,從1開始計算; 第四個參數表示下邊區域可見的首行序號,也是從1開始計算,這里是凍結列,所以為0;
舉例說明也許更好理解,將各參數設置為如下:
sheet1.CreateFreezePane(2,0,5,0);
得到的效果如下圖:
注意圖中C、D和E列默認是看不到的,滾動才看得到,這就是第三個參數5起了作用,是不是很好理解了呢:)
接下來,看一下凍結行的效果。將上面的代碼稍作修改:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFRow row1 = sheet1.CreateRow(0); row1.CreateCell(0).SetCellValue("凍結行"); sheet1.CreateFreezePane(0, 1, 0, 1);
執行后生成的Excel文件效果見下圖:
那么,如果要行和列同時凍結該怎么做呢?聰明的你一定能想得到,呵呵~~
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
有些時候,我們需要網格線,而有些時候我們不需要,這取決於實際的業務需求。前兩天inmegin兄就問我,怎么把網格給去掉,因為他們要把Excel文檔當Word使,也許是因為Excel排版方便吧。
Excel中的網格線設置是以表(Sheet)為單位進行管理的,這也就意味着你可以讓一個表顯示網格線,而另一個表不顯示,這是不沖突的。
在Excel 2007中,我們通常用“工作表選項”面板來設置這個屬性:
在面板中,你會發現有2個多選框,一個是查看,一個是打印,也就是說Excel是把查看和打印網格線作為兩個設置來處理的,存儲的Record也是不同的。
在NPOI中,如果要讓網格線在查看時顯示/隱藏,你可以HSSFSheet.DisplayGridlines屬性,默認值為true(這也是為什么默認情況下我們能夠看到網格線)。下面的代碼就是讓網格線在查看時不可見的:
HSSFWorkbook hssfworkbook = new HSSFWorkbook();
HSSFSheet s1 = hssfworkbook.CreateSheet("Sheet1");
s1.DisplayGridlines = false;
如果要在打印時顯示/隱藏網格線,你可以用HSSFSheet.IsGridlinesPrinted屬性,默認值為false(這就是默認情況下打印看不到網格線的原因)。代碼和上面差不多:
s1.IsGridsPrinted = true;
上面的代碼將在打印時顯示網格線,打印的效果如下所示。
在此也提醒大家,如果這個Excel最終客戶有打印意向,可別忘了把IsGridPrinted屬性也設置上。
相關范例可以參考NPOI 1.2正式版中的DisplayGridlinesInXls項目。
有些時候,我們可能希望生成的Excel文件在被打開的時候自動將焦點定位在某個單元格或是選中某個區域中。在NPOI中可以通過SetAsActiveCell和SetActiveCellRange等幾個方法實現。
首先我們看一下設置初始視圖中選中某個單元格的方法:
//use HSSFCell.SetAsActiveCell() to select B6 as the active column HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet A"); CreateCellArray(sheet1); sheet1.GetRow(5).GetCell(1).SetAsActiveCell(); //set TopRow and LeftCol to make B6 the first cell in the visible area sheet1.TopRow = 5; sheet1.LeftCol = 1;
其中CreateCellArray(sheet1)方法用來寫示范數據,其代碼為(下同):
static void CreateCellArray(HSSFSheet sheet) { for (int i = 0; i < 300; i++) { HSSFRow row=sheet.CreateRow(i); for (int j = 0; j < 150; j++) { HSSFCell cell = row.CreateCell(j); cell.SetCellValue(i*j); } } }
生成的Excel打開時效果如下,注意B6為默認選中狀態,TopRow和LeftCol設置B6為當前可見區域的第一個單元格:
如果不設置TopRow和LeftCol屬性,默認的可見域的第一個單元格為A1,如下是另一種設置活動單元格的方法,但沒有設置此Sheet的TopRow和LeftCol:
HSSFSheet sheet2 = hssfworkbook.CreateSheet("Sheet B"); sheet2.Sheet.SetActiveCell(1, 5);
對應生成的Excel顯示為:
除了設置某個單元格為選中狀態外,還NPOI可以設置某個區域為選中狀態:
//use Sheet.SetActiveCellRange to select a cell range HSSFSheet sheet3 = hssfworkbook.CreateSheet("Sheet C"); CreateCellArray(sheet3); sheet3.Sheet.SetActiveCellRange(2, 5, 1, 5);
以上代碼設置了Sheet C的選中區域為B3:F6:
還有更強大的,設置多個選中區域:
//use Sheet.SetActiveCellRange to select multiple cell ranges HSSFSheet sheet4 = hssfworkbook.CreateSheet("Sheet D"); CreateCellArray(sheet4); List<CellRangeAddress8Bit> cellranges = new List<CellRangeAddress8Bit>(); cellranges.Add(new CellRangeAddress8Bit(1,3,2,5)); cellranges.Add(new CellRangeAddress8Bit(6,7,8,9)); sheet4.Sheet.SetActiveCellRange(cellranges,1,6,9);
如果一個Excel文件中有多個Sheet,還可以通過如下語句設置打開時的初始Sheet:
hssfworkbook.ActiveSheetIndex = 2;
在有些情況下(比如Excel引入),我們可能不允許用戶在Excel隨意輸入一些無效數據,這時就要在模板中加一些數據有效性的驗證。在Excel中,設置數據有效性的方步驟如下: (1)先選定一個區域; 數據有效性”中設置數據有效性驗證(如圖)。à(2)在菜單“數據
同樣,利用NPOI,用代碼也可以實現:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1");
sheet1.CreateRow(0).CreateCell(0).SetCellValue("日期列"); CellRangeAddressList regions1 = new CellRangeAddressList(1, 65535, 0, 0); DVConstraint constraint1 = DVConstraint.CreateDateConstraint(DVConstraint.OperatorType.BETWEEN, "1900-01-01", "2999-12-31", "yyyy-MM-dd"); HSSFDataValidation dataValidate1 = new HSSFDataValidation(regions1, constraint1); dataValidate1.CreateErrorBox("error", "You must input a date."); sheet1.AddValidationData(dataValidate1);
上面是一個在第一列要求輸入1900-1-1至2999-12-31之間日期的有效性驗證的例子,生成的Excel效果如下,當輸入非法時將給出警告:
下面對剛才用到的幾個方法加以說明: CellRangeAddressList類表示一個區域,構造函數中的四個參數分別表示起始行序號,終止行序號,起始列序號,終止列序號。所以第一列所在區域就表示為:
//所有序號都從零算起,第一行標題行除外,所以第一個參數是1,65535是一個Sheet的最大行數 new CellRangeAddressList(1, 65535, 0, 0);
另外,CreateDateConstraint的第一個參數除了設置成DVConstraint.OperatorType.BETWEEN外,還可以設置成如下一些值,大家可以自己一個個去試看看效果:
最后,dataValidate1.CreateErrorBox(title,text),用來創建出錯時的提示信息。第一個參數表示提示框的標題,第二個參數表示提示框的內容。
理解了上面這些,創建一個整數類型的有效性驗證也不難實現:
sheet1.CreateRow(0).CreateCell(1).SetCellValue("數值列"); CellRangeAddressList regions2 = new CellRangeAddressList(1, 65535, 1, 1); DVConstraint constraint2 = DVConstraint.CreateNumericConstraint(DVConstraint.ValidationType.INTEGER,DVConstraint.OperatorType.BETWEEN, "0", "100"); HSSFDataValidation dataValidate2 = new HSSFDataValidation(regions2, constraint2); dataValidate2.CreateErrorBox("error", "You must input a numeric between 0 and 100."); sheet1.AddValidationData(dataValidate2);
生成的Excel效果為:
下一節我們將學習利用數據有效性創建下拉列表的例子。
上一節我們講了簡單的數據有效性驗證,這一節我們學習一下數據有效性的另一個應用--下拉列表。在Excel中,並沒有類似Web中的下拉控件,其下拉效果是通過數據有效性來實現的。設置步驟為: (1)選定一個要生成下拉列表的區域; (2)設置數據有效性為序列,並在來源中填充可選下拉的值,用“,”隔開(如圖)。
對應的效果為:
同樣,利用NPOI代碼也可以實現上面的效果:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1");
CellRangeAddressList regions = new CellRangeAddressList(0, 65535, 0, 0); DVConstraint constraint = DVConstraint.CreateExplicitListConstraint(new string[] { "itemA", "itemB", "itemC" }); HSSFDataValidation dataValidate = new HSSFDataValidation(regions, constraint); sheet1.AddValidationData(dataValidate);
下面對代碼作一下簡要說明: 先設置一個需要提供下拉的區域,關於CellRangeAddressList構造函數參數的說明請參見上一節:
CellRangeAddressList regions = new CellRangeAddressList(0, 65535, 0, 0);
然后將下拉項作為一個數組傳給CreateExplicitListConstraint作為參數創建一個約束,根據要控制的區域和約束創建數據有效性就可以了。
但是這樣會有一個問題:Excel中允許輸入的序列來源長度最大為255個字符,也就是說當下拉項的總字符串長度超過255是將會出錯。那么如果下拉項很多的情況下應該怎么處理呢?答案是通過引用的方式。步驟如下: 先創建一個Sheet專門用於存儲下拉項的值,並將各下拉項的值寫入其中:
HSSFSheet sheet2 = hssfworkbook.CreateSheet("ShtDictionary"); sheet2.CreateRow(0).CreateCell(0).SetCellValue("itemA"); sheet2.CreateRow(1).CreateCell(0).SetCellValue("itemB"); sheet2.CreateRow(2).CreateCell(0).SetCellValue("itemC");
然后定義一個名稱,指向剛才創建的下拉項的區域:
HSSFName range = hssfworkbook.CreateName(); range.Reference = "ShtDictionary!$A1:$A3"; range.NameName = "dicRange";
最后,設置數據約束時指向這個名稱而不是字符數組:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); CellRangeAddressList regions = new CellRangeAddressList(0, 65535, 0, 0);
DVConstraint constraint = DVConstraint.CreateFormulaListConstraint("dicRange"); HSSFDataValidation dataValidate = new HSSFDataValidation(regions, constraint); sheet1.AddValidationData(dataValidate);
執行這段代碼,生成的Excel效果如下:
在名稱管理器中會發現有一個名為"dicRange"的名稱,指向"ShtDictionary!$A1:$A3"的下拉項區域:
在數據有效性中會發現來源變成了"=dicRange",指向上面定義的名稱。而不是以前的"itemA,itemB,itemC":
NPOI 1.2教程 – 3.1 基於.xls模板生成Excel文件
作者:Tony Qu
NPOI官方網站:http://npoi.codeplex.com/
NPOI QQ交流群: 78142590
盡管NPOI能夠從頭開始生成Excel文件,但在實際生產環境中有很多現成的表格,我們不可能沒事就去從頭開始生成一個Excel,更多時候我們更願意選擇比較偷懶的方法——那就是用模板文件。NPOI一大特色之一就是能夠輕松讀取Office Excel 97-2003的格式,即使里面有NPOI不支持的VBA宏、圖表以及Pivot表之類的高級記錄,NPOI也能夠保證不丟失數據(說實話,要完全能夠識別所有的Excel內部記錄幾乎是不可能的,更何況如今又多出了Office Excel 2007 binary file,即.xlsb)。
現在我們轉入正題,出於演示目的,我做了一個簡單的銷售量表,里面應用了文字顏色、背景色、文本居中、公式、千分位分隔符、邊框等效果,當然實際的生產環境里可能還有更加復雜的Excel模板。如下圖
我們的程序就是要填充12個月的銷售量,Total能夠自動根據填充的值計算出總量。
(這里要提一下,以往如果我們用HTML方式輸出xls,我們必須在服務器端做Total計算,並且這個值在下載后永遠都是靜態的,沒有公式,即使用戶要修改里面的數據,總值也不會改變。這也是為什么NPOI一直提倡生成真正的Excel文件。)
代碼其實很簡單:
//read the template via FileStream, it is suggested to use FileAccess.Read to prevent file lock.
//book1.xls is an Excel-2007-generated file, so some new unknown BIFF records are added.
FileStream file = new FileStream(@"template/book1.xls", FileMode.Open,FileAccess.Read);
HSSFWorkbook hssfworkbook = new HSSFWorkbook(file); HSSFSheet sheet1 = hssfworkbook.GetSheet("Sheet1"); sheet1.GetRow(1).GetCell(1).SetCellValue(200200); sheet1.GetRow(2).GetCell(1).SetCellValue(300); sheet1.GetRow(3).GetCell(1).SetCellValue(500050); sheet1.GetRow(4).GetCell(1).SetCellValue(8000); sheet1.GetRow(5).GetCell(1).SetCellValue(110); sheet1.GetRow(6).GetCell(1).SetCellValue(100); sheet1.GetRow(7).GetCell(1).SetCellValue(200); sheet1.GetRow(8).GetCell(1).SetCellValue(210); sheet1.GetRow(9).GetCell(1).SetCellValue(2300); sheet1.GetRow(10).GetCell(1).SetCellValue(240); sheet1.GetRow(11).GetCell(1).SetCellValue(180123); sheet1.GetRow(12).GetCell(1).SetCellValue(150);
//Force excel to recalculate all the formula while open
sheet1.ForceFormulaRecalculation = true;
FileStream file = new FileStream(@"test.xls", FileMode.Create); hssfworkbook.Write(file); file.Close();
首先打開模板文件時要使用FileAccess.Read,這樣可以保證文件不被占用。
這里的ForceFormulaRecalculation是強制要求Excel在打開時重新計算的屬性,在擁有公式的xls文件中十分有用,大家使用時可別忘了設。
是不是比你想象的簡單?你甚至不用去了解它是在何時讀取文件內容的,對於NPOI的使用者來說基本上和讀取普通文件沒有什么兩樣。
最終生成的效果如下所示:
發覺沒,就連千分位分隔符也都保留着,一切就像人工填寫的一樣。
本范例完整代碼請見NPOI.Examples中的GenerateXlsFromXlsTemplate項目。
還記得小學時候學的九九乘法表嗎?這節我們一起學習利用NPOI通過C#代碼生成一張Excel的九九乘法表。要生成九九乘法表,循環肯定是少不了的,如下:
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFRow row; HSSFCell cell; for (int rowIndex = 0; rowIndex < 9; rowIndex++) { row = sheet1.CreateRow(rowIndex); for (int colIndex = 0; colIndex <= rowIndex; colIndex++) { cell = row.CreateCell(colIndex); cell.SetCellValue(String.Format("{0}*{1}={2}", rowIndex + 1, colIndex + 1, (rowIndex + 1) * (colIndex + 1))); } }
代碼其實很簡單,就是循環調用cell.SetCellValue(str)寫入9行數據,每一行寫的單元格數量隨行數遞增。執行完后生成的Excel樣式如下:
完整的代碼如下:
Code using System; using System.Collections.Generic; using System.Linq; using System.Text; using NPOI.HSSF.UserModel; using System.IO; using NPOI.HPSF;
namespace TimesTables { public class Program { static HSSFWorkbook hssfworkbook;
static void Main(string[] args) { InitializeWorkbook();
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFRow row; HSSFCell cell; for (int rowIndex = 0; rowIndex < 9; rowIndex++) { row = sheet1.CreateRow(rowIndex); for (int colIndex = 0; colIndex <= rowIndex; colIndex++) { cell = row.CreateCell(colIndex); cell.SetCellValue(String.Format("{0}*{1}={2}", rowIndex + 1, colIndex + 1, (rowIndex + 1) * (colIndex + 1))); } }
WriteToFile(); }
static void WriteToFile() { //Write the stream data of workbook to the root directory FileStream file = new FileStream(@"test.xls", FileMode.Create); hssfworkbook.Write(file); file.Close(); }
static void InitializeWorkbook() { hssfworkbook = new HSSFWorkbook();
//create a entry of DocumentSummaryInformation DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation(); dsi.Company = "NPOI Team"; hssfworkbook.DocumentSummaryInformation = dsi;
//create a entry of SummaryInformation SummaryInformation si = PropertySetFactory.CreateSummaryInformation(); si.Subject = "NPOI SDK Example"; hssfworkbook.SummaryInformation = si; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NPOI.HSSF.UserModel; using System.IO; using NPOI.HPSF;
namespace TimesTables { public class Program { static HSSFWorkbook hssfworkbook;
static void Main(string[] args) { InitializeWorkbook();
HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFRow row; HSSFCell cell; for (int rowIndex = 0; rowIndex < 9; rowIndex++) { row = sheet1.CreateRow(rowIndex); for (int colIndex = 0; colIndex <= rowIndex; colIndex++) { cell = row.CreateCell(colIndex); cell.SetCellValue(String.Format("{0}*{1}={2}", rowIndex + 1, colIndex + 1, (rowIndex + 1) * (colIndex + 1))); } }
WriteToFile(); }
static void WriteToFile() { //Write the stream data of workbook to the root directory FileStream file = new FileStream(@"test.xls", FileMode.Create); hssfworkbook.Write(file); file.Close(); }
static void InitializeWorkbook() { hssfworkbook = new HSSFWorkbook();
//create a entry of DocumentSummaryInformation DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation(); dsi.Company = "NPOI Team"; hssfworkbook.DocumentSummaryInformation = dsi;
//create a entry of SummaryInformation SummaryInformation si = PropertySetFactory.CreateSummaryInformation(); si.Subject = "NPOI SDK Example"; hssfworkbook.SummaryInformation = si; } } }
這一節,我們將綜合NPOI的常用功能(包括創建和填充單元格、合並單元格、設置單元格樣式和利用公式),做一個工資單的實例。先看創建標題行的代碼:
//寫標題文本 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFCell cellTitle = sheet1.CreateRow(0).CreateCell(0); cellTitle.SetCellValue("XXX公司2009年10月工資單");
//設置標題行樣式 HSSFCellStyle style = hssfworkbook.CreateCellStyle(); style.Alignment = HSSFCellStyle.ALIGN_CENTER; HSSFFont font = hssfworkbook.CreateFont(); font.FontHeight = 20 * 20; style.SetFont(font);
cellTitle.CellStyle = style;
//合並標題行 sheet1.AddMergedRegion(new Region(0, 0, 1, 6));
其中用到了我們前面講的設置單元格樣式和合並單元格等內容。接下來我們循環創建公司每個員工的工資單:
DataTable dt=GetData(); HSSFRow row; HSSFCell cell; HSSFCellStyle celStyle=getCellStyle();
HSSFPatriarch patriarch = sheet1.CreateDrawingPatriarch(); HSSFClientAnchor anchor; HSSFSimpleShape line; int rowIndex; for (int i = 0; i < dt.Rows.Count; i++) { //表頭數據 rowIndex = 3 * (i + 1); row = sheet1.CreateRow(rowIndex);
cell = row.CreateCell(0); cell.SetCellValue("姓名"); cell.CellStyle = celStyle;
cell = row.CreateCell(1); cell.SetCellValue("基本工資"); cell.CellStyle = celStyle;
cell = row.CreateCell(2); cell.SetCellValue("住房公積金"); cell.CellStyle = celStyle;
cell = row.CreateCell(3); cell.SetCellValue("績效獎金"); cell.CellStyle = celStyle;
cell = row.CreateCell(4); cell.SetCellValue("社保扣款"); cell.CellStyle = celStyle;
cell = row.CreateCell(5); cell.SetCellValue("代扣個稅"); cell.CellStyle = celStyle;
cell = row.CreateCell(6); cell.SetCellValue("實發工資"); cell.CellStyle = celStyle;
DataRow dr = dt.Rows[i]; //設置值和計算公式 row = sheet1.CreateRow(rowIndex + 1); cell = row.CreateCell(0); cell.SetCellValue(dr["FName"].ToString()); cell.CellStyle = celStyle;
cell = row.CreateCell(1); cell.SetCellValue((double)dr["FBasicSalary"]); cell.CellStyle = celStyle;
cell = row.CreateCell(2); cell.SetCellValue((double)dr["FAccumulationFund"]); cell.CellStyle = celStyle;
cell = row.CreateCell(3); cell.SetCellValue((double)dr["FBonus"]); cell.CellStyle = celStyle;
cell = row.CreateCell(4); cell.SetCellFormula(String.Format("$B{0}*0.08",rowIndex+2)); cell.CellStyle = celStyle;
cell = row.CreateCell(5); cell.SetCellFormula(String.Format("SUM($B{0}:$D{0})*0.1",rowIndex+2)); cell.CellStyle = celStyle;
cell = row.CreateCell(6); cell.SetCellFormula(String.Format("SUM($B{0}:$D{0})-SUM($E{0}:$F{0})",rowIndex+2)); cell.CellStyle = celStyle;
//繪制分隔線 sheet1.AddMergedRegion(new Region(rowIndex+2, 0, rowIndex+2, 6)); anchor = new HSSFClientAnchor(0, 125, 1023, 125, 0, rowIndex + 2, 6, rowIndex + 2); line = patriarch.CreateSimpleShape(anchor); line.ShapeType = HSSFSimpleShape.OBJECT_TYPE_LINE; line.LineStyle = HSSFShape.LINESTYLE_DASHGEL;
}
其中為了文件打印為單元格增加了黑色邊框的樣式(如果不設置邊框樣式,打印出來后是沒有邊框的)。另外,注意循環過程中excel中的行號隨數據源中的行號變化處理。完整代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NPOI.HSSF.UserModel; using System.IO; using NPOI.HPSF; using NPOI.HSSF.Util; using System.Data;
namespace Payroll { public class Program { static HSSFWorkbook hssfworkbook;
static void Main(string[] args) { InitializeWorkbook();
//寫標題文本 HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); HSSFCell cellTitle = sheet1.CreateRow(0).CreateCell(0); cellTitle.SetCellValue("XXX公司2009年10月工資單");
//設置標題行樣式 HSSFCellStyle style = hssfworkbook.CreateCellStyle(); style.Alignment = HSSFCellStyle.ALIGN_CENTER; HSSFFont font = hssfworkbook.CreateFont(); font.FontHeight = 20 * 20; style.SetFont(font);
cellTitle.CellStyle = style;
//合並標題行 sheet1.AddMergedRegion(new Region(0, 0, 1, 6));
DataTable dt=GetData(); HSSFRow row; HSSFCell cell; HSSFCellStyle celStyle=getCellStyle();
HSSFPatriarch patriarch = sheet1.CreateDrawingPatriarch(); HSSFClientAnchor anchor; HSSFSimpleShape line; int rowIndex; for (int i = 0; i < dt.Rows.Count; i++) { //表頭數據 rowIndex = 3 * (i + 1); row = sheet1.CreateRow(rowIndex);
cell = row.CreateCell(0); cell.SetCellValue("姓名"); cell.CellStyle = celStyle;
cell = row.CreateCell(1); cell.SetCellValue("基本工資"); cell.CellStyle = celStyle;
cell = row.CreateCell(2); cell.SetCellValue("住房公積金"); cell.CellStyle = celStyle;
cell = row.CreateCell(3); cell.SetCellValue("績效獎金"); cell.CellStyle = celStyle;
cell = row.CreateCell(4); cell.SetCellValue("社保扣款"); cell.CellStyle = celStyle;
cell = row.CreateCell(5); cell.SetCellValue("代扣個稅"); cell.CellStyle = celStyle;
cell = row.CreateCell(6); cell.SetCellValue("實發工資"); cell.CellStyle = celStyle;
DataRow dr = dt.Rows[i]; //設置值和計算公式 row = sheet1.CreateRow(rowIndex + 1); cell = row.CreateCell(0); cell.SetCellValue(dr["FName"].ToString()); cell.CellStyle = celStyle;
cell = row.CreateCell(1); cell.SetCellValue((double)dr["FBasicSalary"]); cell.CellStyle = celStyle;
cell = row.CreateCell(2); cell.SetCellValue((double)dr["FAccumulationFund"]); cell.CellStyle = celStyle;
cell = row.CreateCell(3); cell.SetCellValue((double)dr["FBonus"]); cell.CellStyle = celStyle;
cell = row.CreateCell(4); cell.SetCellFormula(String.Format("$B{0}*0.08",rowIndex+2)); cell.CellStyle = celStyle;
cell = row.CreateCell(5); cell.SetCellFormula(String.Format("SUM($B{0}:$D{0})*0.1",rowIndex+2)); cell.CellStyle = celStyle;
cell = row.CreateCell(6); cell.SetCellFormula(String.Format("SUM($B{0}:$D{0})-SUM($E{0}:$F{0})",rowIndex+2)); cell.CellStyle = celStyle;
//繪制分隔線 sheet1.AddMergedRegion(new Region(rowIndex+2, 0, rowIndex+2, 6)); anchor = new HSSFClientAnchor(0, 125, 1023, 125, 0, rowIndex + 2, 6, rowIndex + 2); line = patriarch.CreateSimpleShape(anchor); line.ShapeType = HSSFSimpleShape.OBJECT_TYPE_LINE; line.LineStyle = HSSFShape.LINESTYLE_DASHGEL;
}
WriteToFile(); }
static DataTable GetData() { DataTable dt = new DataTable(); dt.Columns.Add("FName",typeof(System.String)); dt.Columns.Add("FBasicSalary",typeof(System.Double)); dt.Columns.Add("FAccumulationFund", typeof(System.Double)); dt.Columns.Add("FBonus", typeof(System.Double));
dt.Rows.Add("令狐沖", 6000, 1000, 2000); dt.Rows.Add("任盈盈", 7000, 1000, 2500); dt.Rows.Add("林平之", 5000, 1000, 1500); dt.Rows.Add("岳靈珊", 4000, 1000, 900); dt.Rows.Add("任我行", 4000, 1000, 800); dt.Rows.Add("風清揚", 9000, 5000, 3000);
return dt; }
static HSSFCellStyle getCellStyle() { HSSFCellStyle cellStyle = hssfworkbook.CreateCellStyle(); cellStyle.BorderBottom = HSSFCellStyle.BORDER_THIN; cellStyle.BorderLeft = HSSFCellStyle.BORDER_THIN; cellStyle.BorderRight = HSSFCellStyle.BORDER_THIN; cellStyle.BorderTop = HSSFCellStyle.BORDER_THIN; return cellStyle; }
static void WriteToFile() { //Write the stream data of workbook to the root directory FileStream file = new FileStream(@"test.xls", FileMode.Create); hssfworkbook.Write(file); file.Close(); }
static void InitializeWorkbook() { hssfworkbook = new HSSFWorkbook();
//create a entry of DocumentSummaryInformation DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation(); dsi.Company = "NPOI Team"; hssfworkbook.DocumentSummaryInformation = dsi;
//create a entry of SummaryInformation SummaryInformation si = PropertySetFactory.CreateSummaryInformation(); si.Subject = "NPOI SDK Example"; hssfworkbook.SummaryInformation = si; } } }
生成的Excel文件樣式如下:
我們知道,搜索引擎最擅長處理的就是文本,而Excel中的內容並不是以文本方式存儲的。那么如果想要搜索引擎爬蟲能夠抓取到Excel中的內容是比較困難的,除非搜索引擎爬蟲對Excel格式進行專門的處理。那么有沒有辦法解決此問題呢?有,通過NPOI將Excel內容文本化!
如下,有這樣一張Excel,如果想讓它被搜索引擎收錄,常用的方式是以HTML形式展現,但將一個個這樣的Excel手工做成HTML頁面顯然比較麻煩。接下來,我們將提供一種方案,自動將Excel中的內容以HTML形式展現。
其實基本思想也很簡單,就是通過NPOI讀取每個Cell中的內容,然后以HTML的形式輸出。但要保證輸出的HTML頁面布局與Excel中的一致,還有點小技巧。下面是構造Table的代碼:
private HSSFSheet sht; protected String excelContent;
protected void Page_Load(object sender, EventArgs e) { HSSFWorkbook wb = new HSSFWorkbook(new FileStream(Server.MapPath("App_Data/quotation.xls"), FileMode.Open)); sht = wb.GetSheet("Sheet1");
//取行Excel的最大行數 int rowsCount = sht.PhysicalNumberOfRows; //為保證Table布局與Excel一樣,這里應該取所有行中的最大列數(需要遍歷整個Sheet)。 //為少一交全Excel遍歷,提高性能,我們可以人為把第0行的列數調整至所有行中的最大列數。 int colsCount = sht.GetRow(0).PhysicalNumberOfCells;
int colSpan; int rowSpan; bool isByRowMerged;
StringBuilder table = new StringBuilder(rowsCount * 32);
table.Append("<table border='1px'>"); for (int rowIndex = 0; rowIndex < rowsCount; rowIndex++) { table.Append("<tr>"); for (int colIndex = 0; colIndex < colsCount; colIndex++) { GetTdMergedInfo(rowIndex, colIndex, out colSpan, out rowSpan, out isByRowMerged); //如果已經被行合並包含進去了就不輸出TD了。 //注意被合並的行或列不輸出的處理方式不一樣,見下面一處的注釋說明了列合並后不輸出TD的處理方式。 if (isByRowMerged) { continue; } table.Append("<td"); if (colSpan > 1) table.Append(string.Format(" colSpan={0}", colSpan)); if (rowSpan > 1) table.Append(string.Format(" rowSpan={0}", rowSpan)); table.Append(">");
table.Append(sht.GetRow(rowIndex).GetCell(colIndex));
//列被合並之后此行將少輸出colSpan-1個TD。 if (colSpan > 1) colIndex += colSpan - 1;
table.Append("</td>");
} table.Append("</tr>"); } table.Append("</table>");
this.excelContent = table.ToString(); }
其中用到的GetTdMergedInfo方法代碼如下:
/// <summary> /// 獲取Table某個TD合並的列數和行數等信息。與Excel中對應Cell的合並行數和列數一致。 /// </summary> /// <param name="rowIndex">行號</param> /// <param name="colIndex">列號</param> /// <param name="colspan">TD中需要合並的行數</param> /// <param name="rowspan">TD中需要合並的列數</param> /// <param name="rowspan">此單元格是否被某個行合並包含在內。如果被包含在內,將不輸出TD。</param> /// <returns></returns> private void GetTdMergedInfo(int rowIndex, int colIndex, out int colspan, out int rowspan, out bool isByRowMerged) { colspan = 1; rowspan = 1; isByRowMerged = false; int regionsCuont = sht.NumMergedRegions; Region region; for (int i = 0; i < regionsCuont; i++) { region = sht.GetMergedRegionAt(i); if (region.RowFrom == rowIndex && region.ColumnFrom == colIndex) { colspan = region.ColumnTo - region.ColumnFrom + 1; rowspan = region.RowTo - region.RowFrom + 1;
return; } else if (rowIndex > region.RowFrom && rowIndex <= region.RowTo && colIndex>=region.ColumnFrom && colIndex<=region.ColumnTo) { isByRowMerged = true; } } }
最后在apsx頁面中輸出構建好的Table:
<%=excelContent %>
執行效果如下:
我們發現,與Excel中的布局完全一樣(這里沒有處理單元格的樣式,只處理了內容,有興趣的讀者也可以將Excel中單元格的樣式也應用在HTML中)。這里為保證布局一致,主要是將Excel中的Region信息解析成Table的colSpan和rowSpan屬性,如果對這兩個屬性不太了解,可以結合以下代碼和示例加以了解:
<table width="300px" border="1px"> <tr> <td colspan="2" rowspan="2">0,0</td> <td>0,3</td> </tr> <tr> <td>1,3</td> </tr> <tr> <td rowspan="2">2,0</td> <td colspan="2">2,1</td> </tr> <tr> <td>3,1</td> <td>3,2</td> </tr> </table>
以上HTML代碼對應的Table展現為:
在NPOI中,本身並不支持Chart等高級對象的創建,但通過l模板的方式可以巧妙地利用Excel強大的透視和圖表功能,請看以下例子。
首先建立模板文件,定義兩列以及指向此區域的名稱“sales”:
創建數據表,數據來源填入剛才定義的區域:
最后生成的數據透視表所在Sheet的樣式如下:
至此,模板已經建好,另存為“D:\MyProject\NPOIDemo\Chart\Book2.xls”。我們發現,模板就相當於一個“空架子”,僅僅有操作方式沒並沒有任何數據。下一步,我們往這個“空架子”中填入數據。我們通過如下代碼往這個“空架子”中寫入數據:
static void Main(string[] args) { HSSFWorkbook wb = new HSSFWorkbook(new FileStream(@"D:\MyProject\NPOIDemo\Chart\Book2.xls", FileMode.Open));
HSSFSheet sheet1 = wb.GetSheet("Sheet1");
HSSFRow row = sheet1.CreateRow(1); row.CreateCell(0).SetCellValue("令狐沖"); row.CreateCell(1).SetCellValue(50000);
row = sheet1.CreateRow(2); row.CreateCell(0).SetCellValue("任盈盈"); row.CreateCell(1).SetCellValue(30000);
row = sheet1.CreateRow(3); row.CreateCell(0).SetCellValue("風清揚"); row.CreateCell(1).SetCellValue(80000);
row = sheet1.CreateRow(4); row.CreateCell(0).SetCellValue("任我行"); row.CreateCell(1).SetCellValue(20000);
//Write the stream data of workbook to the root directory FileStream file = new FileStream(@"test.xls", FileMode.Create); wb.Write(file); file.Close(); }
打開生成的test.xls文件,發現數據已經被填進去了:
再看數據透視表,也有數據了:
總結: Excel有着強大的報表透視和圖表功能,而且簡單易用,利用NPOI,可以對其進行充分利用。在做圖形報表、透視報表時將非常有用!
作者:Tony Qu
官方網站:http://npoi.codeplex.com | NPOI QQ交流群: 78142590
NPOI之所以強大,並不是因為它支持導出Excel,而是因為它支持導入Excel,並能“理解”OLE2文檔結構,這也是其他一些Excel讀寫庫比較弱的方面。通常,讀入並理解結構遠比導出來得復雜,因為導入你必須假設一切情況都是可能的,而生成你只要保證滿足你自己需求就可以了,如果把導入需求和生成需求比做兩個集合,那么生成需求通常都是導入需求的子集,這一規律不僅體現在Excel讀寫庫中,也體現在pdf讀寫庫中,目前市面上大部分的pdf庫僅支持生成,不支持導入。
如果你不相信NPOI能夠很好的理解OLE2文檔格式,那就去下載POIFS Brower。具體可以參考這篇文章的介紹:Office文件格式解惑。當然單單理解OLE2是不夠的,因為Excel文件格式是BIFF,但BIFF是以OLE2為基礎的,做個很形象的比喻就是:OLE2相當於磁盤的FAT格式,BIFF相當於文件和文件夾。NPOI負責理解BIFF格式的代碼基本都在HSSF命名空間里面。
好了,剛才廢話了一會兒,主要是給大家打打基礎,現在進入正題。
本文將以DataTable為容器讀入某xls的第一個工作表的數據(最近群里面很多人問這個問題)。
在開始之前,我們先來補些基礎知識。每一個xls都對應一個唯一的HSSFWorkbook,每一個HSSFWorkbook會有若干個HSSFSheet,而每一個HSSFSheet包含若干HSSFRow(Excel 2003中不得超過65535行),每一個HSSFRow又包含若干個HSSFCell(Excel 2003中不得超過256列)。
為了遍歷所有的單元格,我們就得獲得某一個HSSFSheet的所有HSSFRow,通常可以用HSSFSheet.GetRowEnumerator()。如果要獲得某一特定行,可以直接用HSSFSheet.GetRow(rowIndex)。另外要遍歷我們就必須知道邊界,有一些屬性我們是可以用的,比如HSSFSheet.FirstRowNum(工作表中第一個有數據行的行號)、HSSFSheet.LastRowNum(工作表中最后一個有數據行的行號)、HSSFRow.FirstCellNum(一行中第一個有數據列的列號)、HSSFRow.LastCellNum(一行中最后一個有數據列的列號)。
基礎知識基本上補得差不多了,現在開工!
首先我們要准備一個用於打開文件流的函數InitializeWorkbook,由於文件讀完后就沒用了,所以這里直接用using(養成好習慣,呵呵)。
HSSFWorkbook hssfworkbook;
void InitializeWorkbook(string path) { //read the template via FileStream, it is suggested to use FileAccess.Read to prevent file lock.
//book1.xls is an Excel-2007-generated file, so some new unknown BIFF records are added.
using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read)) { hssfworkbook = new HSSFWorkbook(file); } }
接下來我們要開始寫最重要的函數ConvertToDataTable,即把HSSF的數據放到一個DataTable中。
HSSFSheet sheet = hssfworkbook.GetSheetAt(0); System.Collections.IEnumerator rows = sheet.GetRowEnumerator();
while (rows.MoveNext()) { HSSFRow row = (HSSFRow)rows.Current; //TODO::Create DataTable row
for (int i = 0; i < row.LastCellNum; i++) { HSSFCell cell = row.GetCell(i); //TODO::set cell value to the cell of DataTables
}
上面的結構大家都應該能看懂吧,無非就是先遍歷行,再遍歷行中的每一列。這里引出了一個難點,由於Excel的單元格有好幾種類型,類型不同顯示的東西就不同,具體的類型有 布爾型、數值型、文本型、公式型、空白、錯誤。
public enum HSSFCellType { Unknown = -1, NUMERIC = 0, STRING = 1, FORMULA = 2, BLANK = 3, BOOLEAN = 4, ERROR = 5, }
這里的HSSFCellType描述了所有的類型,但細心的朋友可能已經發現了,這里沒有日期型,這是為什么呢?這是因為Excel底層並沒有一定日期型,而是通過數值型來替代,至於如何區分日期和數字,都是由文本顯示的樣式決定的,在NPOI中則是由HSSFDataFormat來處理。為了能夠方便的獲得所需要的類型所對應的文本,我們可以使用HSSFCell.ToString()來處理。
於是剛才的代碼則變成了這樣:
HSSFSheet sheet = hssfworkbook.GetSheetAt(0); System.Collections.IEnumerator rows = sheet.GetRowEnumerator();
DataTable dt = new DataTable(); for (int j = 0; j < 5; j++) { dt.Columns.Add(Convert.ToChar(((int)'A')+j).ToString()); }
while (rows.MoveNext()) { HSSFRow row = (HSSFRow)rows.Current; DataRow dr = dt.NewRow();
for (int i = 0; i < row.LastCellNum; i++) { HSSFCell cell = row.GetCell(i);
if (cell == null) { dr[i] = null; } else
{
dr[i] = cell.ToString();
}
}
dt.Rows.Add(dr);
}
是不是很簡單,呵呵!
當然,如果你要對某個特定的單元格類型做特殊處理,可以通過判HSSFCell.CellType來解決,比如下面的代碼:
switch(cell.CellType) { case HSSFCellType.BLANK: dr[i] = "[null]"; break; case HSSFCellType.BOOLEAN: dr[i] = cell.BooleanCellValue; break; case HSSFCellType.NUMERIC: dr[i] = cell.ToString(); //This is a trick to get the correct value of the cell. NumericCellValue will return a numeric value no matter the cell value is a date or a number.
break; case HSSFCellType.STRING: dr[i] = cell.StringCellValue; break; case HSSFCellType.ERROR: dr[i] = cell.ErrorCellValue; break; case HSSFCellType.FORMULA: default: dr[i] = "="+cell.CellFormula; break; }
這里只是舉個簡單的例子。
完整代碼下載:http://files.cnblogs.com/tonyqus/ImportXlsToDataTable.zip
注意,此代碼中不包括NPOI的assembly,否則文件會很大,所以建議去npoi.codeplex.com下載。
