NPOI 開啟 EXCEL 檔案的範例

NPOI 開啟 EXCEL 檔案的範例

簡單的示範如何透過 NPOI 來開啟 EXCEL 檔案(*.xls 與 *.xlsx)。

首先建立一個 Framework 4.0 的 Windows 專案,然後新增 2 個 Button 控制項、1 個 ListBox 控制項、1 個 Lable 控制項、1 個 DataGridView 控制項和 1 個 OpenFileDialog 控制項。

Button 用來開啟檔案與關閉程式。

Label 用來顯示目前開啟的 Excel 檔案名稱。

ListBox 用來顯示 EXCEL 檔案中的活頁簿資料(Sheet)。

DataGridView 則是來顯示在 ListBox 中選取的活頁簿所存放的資料。

OpenFileDialog 是來選取要 EXCEL 檔案。

 

開啟 EXCEL 檔案

選取 Sheet 後將資料顯示在右方的 DataGridView 之中。

 

程式碼解說

因為在一個 EXCEL 檔案中,有可能會有多個 Sheet,為了避免多次的磁碟 IO 動作,因此採用開啟檔案後,將該 EXCEL 檔案內所有 Sheet 資料都載入到 DataSet 中,而每一個 DataTable 的名稱即為 Sheet Name。

程式先定義一個全域的 DataSet 變數為 dsEXCEL,然後開啟檔案後立即將 DataSet 內所有的 DataTable 清除,同時重新設定 LstBox 的內容。然後再依序將 Sheet 的資料轉入到 DataSet 中。


private void OpenFile_Click(object sender, EventArgs e)
{
    if (dlgSelectFile.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
    CurrentFileName.Text = dlgSelectFile.FileName.Trim();
    dsEXCEL = new DataSet();
    lstSheetName.Items.Clear();
    LoadExcelData(CurrentFileName.Text, dlgSelectFile.FilterIndex);
    dgvSheetData.DataSource = null;
}

private void LoadExcelData(string FullFileName, int FileType)
{
    System.IO.FileStream fsFile = new FileStream(FullFileName, FileMode.Open, FileAccess.Read);
    IWorkbook wbXLS;
    //FileType 定義在 OpenFileDialog 的 Filter 上
    //FilterIndex = 1 為 *.xls,FilterIndex = 2 為 *.xlsx
    if (FileType == 1)
    {
        wbXLS = new HSSFWorkbook(fsFile);
    }
    else
    {
        wbXLS = new XSSFWorkbook(fsFile);
    }

    for (int i = 0; i < wbXLS.NumberOfSheets; i++)
    {
        //依序載入 Sheet 的資料
        lstSheetName.Items.Add(wbXLS.GetSheetName(i));
        dsEXCEL.Tables.Add(LoadSheetData(wbXLS, wbXLS.GetSheetName(i)));
    }
    fsFile.Close();
    fsFile.Dispose();
}

private DataTable LoadSheetData(IWorkbook wbXLS, string SheetName)
{
    DataTable dtSheetData = new DataTable(SheetName);
    ISheet wbSheet = wbXLS.GetSheet(SheetName);

    if (wbSheet.LastRowNum == 0) return dtSheetData;

    //取回最大的 Cell 數量
    int MaxCellCount = 0;
    for (int i = 0; i < wbSheet.LastRowNum; i++)
    {
        if ((wbSheet.GetRow(i) != null) && (wbSheet.GetRow(i).LastCellNum > MaxCellCount)) MaxCellCount = wbSheet.GetRow(i).LastCellNum;
    }

    //建立 DataTable 的 Column
    for (int i = 0; i < MaxCellCount; i++)
    {
        dtSheetData.Columns.Add(Convert10To26(i + 1), typeof(object));
    }

    DataRow tmpAddRow;
    try
    {
        for (int i = 0; i <= wbSheet.LastRowNum; i++)
        {
            tmpAddRow = dtSheetData.NewRow();
            if (wbSheet.GetRow(i) == null)
            {
                dtSheetData.Rows.Add(tmpAddRow);
                continue;
            }

            IRow tRow = wbSheet.GetRow(i);
            foreach (ICell tCell in tRow.Cells)
            {
                tmpAddRow[Convert10To26(tmpCell.ColumnIndex + 1)] = tCell.ToString();
            }

            dtSheetData.Rows.Add(tmpAddRow);
        }
    }
    catch { }
    return dtSheetData;
}

private string Convert10To26(int OriginNo)
{
    if (OriginNo <= 0) return "";
    int Count = 0;
    string strReturn = "";

    while (OriginNo > 0)
    {
        Count = OriginNo % 26;

        if (Count == 0)
        {
            Count = 26;
            OriginNo--;
        }
        strReturn = Convert.ToChar(Count + 64) + strReturn;
        OriginNo /= 26;
    }
    return strReturn;
}

 

轉入到 DataTable 時,並沒有特別去分辨資料格式,所以都使用 object 來接收資料。Column Name 則是仿效 EXCEL 的英文字母標頭,將 ColumnIndex 透過自設函數 Convert10To26 轉為英文字母。載入完成後,切換顯示 Sheet 資料時,就只需要重新將 DataSource Bind 到正確的 DataTable 就行了。


private void lstSheetName_SelectedIndexChanged(object sender, EventArgs e)
{
    if (lstSheetName.SelectedIndex < 0) return;
    dgvSheetData.DataSource = dsEXCEL.Tables[lstSheetName.SelectedIndex];
}

 

這個範例沒有什麼很特別的功能,就只是單純的讀取 *.xls 或 *.xlsx 然後顯示到 DataGridView 上,算是基礎應用吧。


程式是運氣與直覺堆砌而成的奇蹟。
若不具備這兩者,不可能以這樣的工時實現這樣的規格。
修改規格是對奇蹟吐槽的褻瀆行為。
而追加修改則是相信奇蹟還會重現的魯莽行動。