首页 > 解决方案 > C# + Excel 的难点

问题描述

我有个问题。

我有一个 Excel 文件,有时同一客户在 2 行中。但不总是。

它就像: 点击

现在,我想在 C# 中创建一个 DataGrid,它可以将其列在一行中,例如: 单击

我知道更改 Excel 文件会更容易,但假设它不会那样工作(我们得到这样的文件,我们不能更改它)

我知道我也可以制作另一个 Excel 文件并用 Excel 制作(已经这样做了,但我们更需要它作为 C#)

现在我在这一点上:

private void button2_Click(object sender, EventArgs e)
        {

            OpenFileDialog ofd = new OpenFileDialog() { Filter = "Excel Arbeitsmappe |*.xlsx", ValidateNames = true };

            if (ofd.ShowDialog() == DialogResult.OK)
                textBox1.Text = ofd.SafeFileName;

            Excel.Application excelApp = new Excel.Application();
            excelApp.Visible = false;

            string filename = ofd.FileName;

            Excel.Workbook workbook = excelApp.Workbooks.Open(filename);
            Excel.Worksheet worksheet = workbook.Worksheets[1];     
            dataGridView1.ColumnCount = 2;
            dataGridView1.Columns[0].Name = "Number";
            dataGridView1.Columns[1].Name = "Street";
            int rows = worksheet.UsedRange.Rows.Count;

            for (int i = 2; i <= rows; i++)
            {
                string combinehr = worksheet.Cells[i, 150].Text + worksheet.Cells[i, 151].Text;
                dataGridView1.Rows.Add(worksheet.Cells[i,29].Text, combinehr);
            }
        }

我如何扩展它以使其像我想要的那样工作?

我会很感激

(对不起英语)

标签: c#excel

解决方案


参考ExcelDataReaderExcelDataReader.DataSetDataSetExtensions (.Net),您可以很容易地将 Excel 文件读入 a DataSet,然后您只需使用以下逻辑:

输入文件:

输入文件

using System;
using System.Data;
using System.IO;
using System.Linq;
using ExcelDataReader;

public DataTable GetTableFromExcel(string filePath)
{
    DataSet ds = new DataSet();
    using (var stream = System.IO.File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
    {
        using (var reader = ExcelReaderFactory.CreateReader(stream))
        {
            ds = reader.AsDataSet();
        }
    }

    DataTable table = new DataTable();
    table.Columns.Add(new DataColumn("CustomerNr"));
    table.Columns.Add(new DataColumn("Address"));

    // Set column names
    for (int i = 0; i < ds.Tables[0].Columns.Count; i++)
    {
        // DataColumn.ColumnName can't be empty when DataColumn is part
        // of a DataTable (throws ArgumentException)
        string columnName = ds.Tables[0].Rows[0][i].ToString();
        if (string.IsNullOrWhiteSpace(columnName))
        {
            columnName = $"Column{i}";
        }
        ds.Tables[0].Columns[i].ColumnName = columnName;
    }

    // Remove the first row containing the headers
    ds.Tables[0].Rows.Remove(ds.Tables[0].Rows[0]);

    // I don't have the benchmarks with me right now, but I've tested
    // DataTable.Select vs DataTable.AsEnumerable.Select many times
    // and the AsEnumerable method its faster, that's why you need the
    // reference to System.Data.DataSetExtensions
    var enumerableTable = ds.Tables[0].AsEnumerable();

    // list of unique products
    var products = enumerableTable.Select(row => row.Field<string>("Product")).Distinct();

    // Add a column for each product
    foreach (string product in products)
    {
        table.Columns.Add(new DataColumn(product));
    }

    // list of unique customers
    var customerNumbers = enumerableTable.Select(row => row.Field<double>("CustomerNr")).Distinct();
    foreach (var customerNumber in customerNumbers)
    {
        DataRow record = table.NewRow();
        record["CustomerNr"] = customerNumber;
        record["Address"] = enumerableTable.First(row => row.Field<double>("CustomerNr").Equals(customerNumber))[1];

        for (int i = 2; i < table.Columns.Count; i++)
        {
            DataRow product = enumerableTable.FirstOrDefault(row => row.Field<double>("CustomerNr").Equals(customerNumber) 
                && row.Field<string>("Product").Equals(table.Columns[i].ColumnName));
            // Quantity = 0 if product is null
            record[i] = product?["Quantity"] ?? 0;
        }

        table.Rows.Add(record);
    }
    return table;
}

结果DataTable

结果数据表


推荐阅读