目錄
-
概要
博客使用Word發博,發布后,排版會出現很多問題,敬請諒解。另外Word發博代碼格式顯示凌亂,因此相關代碼均使用圖片替代。可加群(.NET 1群:85318032)獲取原始文檔。
很久沒發博客了,因為實在是太忙了(請允許我找個借口)。最近沉淀了很多內容,當然很多都差不多忘記了,不過我還是會在有時間的前提下逐步一一道來吧。最近做了一個批量下單的模板導出,因為訂單中有商品大類和小類的概念,而且類型非常多,為了方便用戶選擇以及確保數據的合法性,因此級聯選擇勢在必行。不過,在此之前,本人就算是在Excel中操作都不會設置下拉,跟別說級聯下拉了,並且關於使用代碼生成級聯下拉這塊,網上並沒有相關的可以值得借鑒的內容,但是無論如何,Excel小白還是要挑戰挑戰的。折騰了一下午,總算搞定,而且順便學會了Excel中的序列和級聯。還是挺有成就感的。鑒於網上這塊有價值的內容不多,於是在此分享此內容以及相關核心代碼。
官方博客:http://www.cnblogs.com/codelove/
相關開源庫地址:https://github.com/xin-lai
交流QQ群(.NET 1群):85318032
交流QQ群(Magicodes開源庫交流群):346487194
Nuget包地址:https://www.nuget.org/packages?q=magicodes
-
磨刀不誤砍柴工——先學會Excel中的操作
首先,我們可以參考這個教程(來自百度經驗):
http://jingyan.baidu.com/article/5553fa82035ce565a23934ba.html
這里有一點需要特別注意的,因為開發人員用的Excel版本都比較高,比如我的是2016,估計一般也是2013吧,特別坑爹的是,網上大部分教程是2010或者以下版本的,而在2013或以上版本微軟將某個菜單的文字改了,如下圖所示的地方:
這個有效性菜單你會發現在高版本無法找到,我找了半天,終於找到了:
就是這個圖標!!!現在叫"數據驗證"!!!
通過以上教程,我們可以學會配置了Excel級聯列表:
數據源如下:
名稱管理如下:
級聯效果如下:
搞定了Excel,我們學到了以下幾個概念:
- 通過名稱管理器,我們可以定義序列,或者叫列表和數據源吧
-
通過數據驗證,我們可以設置當單元格所綁定的序列
-
通過INDIRECT函數,我們可以實現下拉級聯效果:
其實這個級聯的實現的思路很有意思,通過INDIRECT獲取到關聯單元格的值,然后這個值就是關聯列表的序列名稱。
搞懂了以上理論,然后我們再開車。沒有理論,很多時候就是瞎折騰!所以老司機開車不能瞎開,得有理論。
-
利用NPOI生成導入模板
-
其實用NPOI還是用Aspose.Cells,這個都沒關系。畢竟我們掌握了理論,我們有理由相信,這兩位都是好同志。在有RMB的前提下,我們願意支持商業的,沒RMB,開源的也能玩得飛起。好了,至於為什么選擇NPOI,很簡單,因為我們沒錢,而且不喜歡盜版。
現在我來說說思路(思路是高於開發的,很多時候如果做一個東西沒有思路,那就很容易"作死",在開發過程中要有意識的培養自己的思路,一方面是思路的形成可以在很多場景遷移借鑒,另一方面是既保障靈活性、擴展性和嚴謹性的前提下,又少走彎路。最后,思路是自己的,代碼是靠抄的。):
-
設置workbook&sheet
NOPI操作Workbook和Sheet的代碼網上很多,我這里就不過多搬運了,核心代碼為:
HSSFWorkbook workbook = new HSSFWorkbook();//創建workbook
ISheet sheet = workbook.CreateSheet("sheet1");//創建sheet
IRow row = sheet.CreateRow(0);//添加行
row.CreateCell(0).SetCellValue("Test");//單元格寫值
-
生成數據Sheet,並按規則列好
根據上面的代碼,我們根據我們的業務邏輯很容易生成以下內容:
注意這個邏輯!一定要正確,而且對應上!
-
添加名稱,並指定數據范圍(綁定數據源)
核心代碼為:
IName range = workbook.CreateName();//創建名稱
range.NameName = bigCategory.CategoryName;//設置名稱
var colName = GetExcelColumnName(colIndex);//根據序號獲取列名,具體代碼見下文
range.RefersToFormula = string.Format("{0}!${3}${2}:${3}${1}",
sheetName,
smallList.Count.ToString(),
2,
colName);
//設置引用位置
//參數1為引用的Sheet名稱
//參數2為行數(數據行數)
//參數3為起始行數(從第二行開始,忽略列頭,列頭是給我們看的)
//參數4為列名(比如A、B、AA、AB這種)
獲取列名的代碼為:
/// <summary>
/// 獲取Excel列名
/// </summary>
/// <param name="columnNumber">列的序號</param>
/// <returns></returns>
private string GetExcelColumnName(int columnNumber)
{
int dividend = columnNumber;
string columnName = String.Empty;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (int)((dividend - modulo) / 26);
}
return columnName;
}
通過以上代碼,就可以定義Excel中的名稱了,對應如圖所示:
-
寫入大類和小類的數據驗證
基於我們的理解,然后結合NPOI的API,我們很快就可以寫出一下代碼了:
//定義Cell范圍,參數1:起始行數,參數2:結束行數,參數3:起始列數,參數4:結束列數
CellRangeAddressList regions = new CellRangeAddressList(1, 65535, lastBigIndex, lastBigIndex);
//綁定序列地址
DVConstraint constraint = DVConstraint.CreateFormulaListConstraint("產品大類");
//定義數據驗證
HSSFDataValidation dataValidate = new HSSFDataValidation(regions, constraint);
//添加數據驗證
sheet.AddValidationData(dataValidate);
如上面代碼所示,這里是設置大類的數據驗證。如圖:
效果:
設置級聯的數據驗證:
var smallColIndex = i - 1;
//與大類關聯
var colName = GetExcelColumnName(lastBigIndex + 1);
//這里只設置了該列的500行,可以按自己的情況來寫入
for (int j = 1; j <= 500; j++)
{
//定義Cell范圍,參數1:起始行數,參數2:結束行數,參數3:起始列數,參數4:結束列數
//這里的范圍是單個單元格,因為我們的公式用到了具體的單元格地址
CellRangeAddressList regions = new CellRangeAddressList(j, j, smallColIndex, smallColIndex);
//使用INDIRECT函數,這里指定了具體地址
DVConstraint constraint = DVConstraint.CreateFormulaListConstraint(string.Format("INDIRECT(${0}${1})", colName, j + 1));
HSSFDataValidation dataValidate = new HSSFDataValidation(regions, constraint);
sheet.AddValidationData(dataValidate);
}
效果如圖:
好了,整個導出的主要邏輯就是這些。理解了思想,再結合官方API就可以很快搞定了。
整個分享就到這里了,如此看來,Excel還是大有可為的,不通過宏就可以做很多事情。而且Excel還可以直接連接外部數據源,包括OData服務等,有興趣的小伙伴可以研究研究。
本次開車到此結束。