使用 ClosedXML 和 OpenFileDialog 将 Excel 导入 DataGrid

Importing Excel to DataGrid using ClosedXML and OpenFileDialog

我想使用 ClosedXML 将 Excel 文件加载到我的 DataGrid 中。

我有这个方法:

public static DataTable ImportExceltoDataTable(string filePath, string sheetName) {

    using (XLWorkbook wb = new(filePath)) {

        IXLWorksheet ws = wb.Worksheet(1);
        DataTable dt = new();

        bool firstRow = true;
        foreach (IXLRow row in ws.Rows()) {

            if (firstRow) {
                foreach (IXLCell cell in row.Cells()) {
                    dt.Columns.Add(cell.CachedValue.ToString());
                }
                
                firstRow = false;

            } else {
      
                dt.Rows.Add();
                int i = 0;
          
                foreach (IXLCell cell in row.Cells(row.FirstCellUsed().Address.ColumnNumber, row.LastCellUsed().Address.ColumnNumber)) {
                
                    dt.Rows[dt.Rows.Count - 1][i} = cell.CachedValue.ToString();
                    i++;
                }
            }
        }
 
        return dt;
    }
}

在点击事件中,我尝试使用 OpenFileDialog 选择我的文件,见下文:

OpenFileDialog of = new();
of.Filter = "Excel Files | *.xlsx;";
of.Title = "Import Excel file.";

if (of.ShowDialog()==true) {

    dataGrid.ItemsSource = ImportExceltoDataTable("...", "...").DefaultView;
}

但我不知道如何通知 DataTable 我在 OpenFileDialog 中选择了一个文件。 在 DataTable 方法的第一行,我收到以下异常错误:

System.ArgumentException: 'Empty extension is not supported'

有道理...我如何告诉它我选择了哪个文件?

您可能想要 re-think 您阅读 excel 文件的方法。一个可能的问题是 if (firstRow) { … … 语句,它很奇怪并且做出了一个危险的假设。代码“假设”每一列数据“有”一个 header 单元格。换句话说,添加到 DataTable 的列数将取决于在“FIRST”行上找到的单元格数(单元格中有一些文本)。如果一列数据没有 header 单元格怎么办?

因此,如果第一行的单元格右侧有任何行具有 headers 的数据,则 DataTable 的列数将不正确......并且,当代码到达下面 else 部分中的这些单元格……当 i 超出 dt 的列数时,代码很可能会崩溃。

代码需要保证dt有正确的列数以避免上述问题。一种帮助方法是找到定义数据开始的“top-left”单元格(因为它不一定总是工作表中的第一个单元格)和“bottom-right”单元格的两个单元格找到包含数据的“最后一个”单元格。

一旦我们有了这两个单元格(top-left 和 bottom-right)……然后,我们就可以确定 DataTable 中需要多少列……并且……我们几乎可以保证工作表中的所有数据都将适合 DataTable.

下面是使用上述想法的一种可能的解决方案。请注意,下面的代码不使用特定的工作表名称,只是使用给定工作簿中的第一个工作表。

private void Button_Click(object sender, RoutedEventArgs e) {
  OpenFileDialog of = new OpenFileDialog();
  of.Filter = "Excel Files | *.xlsx;";
  of.Title = "Import Excel file.";
  if (of.ShowDialog() == true) {
    dataGrid.ItemsSource = ImportExceltoDataTable(of.FileName).DefaultView;
  }
}

public static DataTable ImportExceltoDataTable(string filePath) {
  using (XLWorkbook wb = new XLWorkbook(filePath)) {
    IXLWorksheet ws = wb.Worksheet(1);
    int tl_Row = ws.FirstCellUsed().Address.RowNumber;
    int tl_Col = ws.FirstCellUsed().Address.ColumnNumber;
    int br_Row = ws.LastCellUsed().Address.RowNumber;
    int br_Col = ws.LastCellUsed().Address.ColumnNumber;
    DataTable dt = new DataTable();
    // add dt columns using the first row of data
    for (int i = tl_Col; i <= br_Col; i++) {
      dt.Columns.Add(ws.Cell(tl_Row, i).CachedValue.ToString());
    }
    IXLRow currentRow;
    // add data from the worksheet to dt - we already used the first row of data for the columns
    for (int dtRow = 0; dtRow < br_Row - tl_Row; dtRow++) {
      currentRow = ws.Row(tl_Row + dtRow + 1);
      dt.Rows.Add();
      for (int dtCol = 0; dtCol < br_Col - tl_Col + 1; dtCol++) {
        dt.Rows[dtRow][dtCol] = currentRow.Cell(tl_Col + dtCol).CachedValue; 
      }
    }
    return dt;
  }
}

我希望这有道理并有所帮助。