วิธีการส่งออก DataTable ไปยัง Excel


107

ฉันจะส่งออกDataTableไปยัง Excel ใน C # ได้อย่างไร ฉันใช้ Windows Forms DataTableมีความเกี่ยวข้องกับDataGridViewการควบคุม ฉันต้องส่งออกบันทึกDataTableไปยัง Excel


วิธีที่ง่ายที่สุดคือทำ foreach loop ที่ซ้อนกันบนรายการและรายการย่อย
Saeid Yazdani

หมายเหตุ: หากคุณกำลังพยายามส่งผ่านค่าจากตารางข้อมูลไปยังวัตถุไปยัง Excel คุณควรจัดการข้อผิดพลาดประเภทข้อมูลด้วย ตัวอย่างเช่น Guids จะฆ่างานของคุณโดยมีข้อยกเว้น HRESULT: 0x800A03EC วิธีแก้ปัญหาอย่างหนึ่งโดยไม่ต้องทดสอบประเภทข้อมูลคือการใช้ "ToString ()" เมื่อเติมข้อมูลวัตถุของคุณ Excel จะแปลงตัวเลขกลับเป็นรูปแบบตัวเลขด้วยตัวมันเอง FlashTrev ตามที่แก้ไขปัญหาวันที่ / เวลาที่เกี่ยวข้อง
u8it

ฉันขอแนะนำคำตอบสำหรับคำถามนี้: stackoverflow.com/a/536699/1367391
Troy Cosentino

คำตอบ:


129

ฉันอยากจะแนะนำClosedXML -

คุณสามารถเปลี่ยน DataTable เป็นแผ่นงาน Excel ด้วยโค้ดที่อ่านได้มาก :

XLWorkbook wb = new XLWorkbook();
DataTable dt = GetDataTableOrWhatever();
wb.Worksheets.Add(dt,"WorksheetName");

นักพัฒนาตอบสนองและเป็นประโยชน์ โครงการได้รับการพัฒนาอย่างแข็งขันและเอกสารประกอบก็ยอดเยี่ยม


7
การเพิ่มไลบรารีอ้างอิง ~ 6 MB จะไม่ทำให้แอปพลิเคชันหนักเพียงเล็กน้อย?
ʞᴉɯ

4
คำถามที่ดี @MicheleVirgilio ฉันยังไม่ได้ทำการทดสอบเพื่อหาปริมาณผลกระทบ แต่สำหรับสิ่งที่คุ้มค่ามันไม่ได้รบกวนฉันในโครงการใด ๆ ที่ฉันใช้อันที่จริงฉันไม่สามารถพูดได้ว่าฉันเคยสังเกตเห็นมัน
hmqcnoesy

รหัสนี้ส่งคืน excel พร้อมคอลัมน์เดียวที่มีค่าClosedXML.Excel.XLWorkbook
เป็นกับดัก

79

ลองใช้รหัสง่ายๆในการแปลง DataTable เป็นไฟล์ excel เป็น csv:

var lines = new List<string>();

string[] columnNames = dataTable.Columns
    .Cast<DataColumn>()
    .Select(column => column.ColumnName)
    .ToArray();

var header = string.Join(",", columnNames.Select(name => $"\"{name}\""));
lines.Add(header);

var valueLines = dataTable.AsEnumerable()
    .Select(row => string.Join(",", row.ItemArray.Select(val => $"\"{val}\"")));

lines.AddRange(valueLines);

File.WriteAllLines("excel.csv", lines);

สิ่งนี้จะเขียนไฟล์ใหม่excel.csvลงในไดเร็กทอรีการทำงานปัจจุบันซึ่งโดยทั่วไปจะอยู่ที่. exe อยู่หรือที่ที่คุณเปิดใช้งาน


1
ช่างเป็นคำตอบที่ยอดเยี่ยมเพื่อน ฉันไม่มีขอบเขตที่จะให้คะแนนโหวตมากกว่าหนึ่งคะแนนสำหรับคำตอบของคุณมิฉะนั้นฉันอาจให้คะแนนมากกว่า 100 คะแนนก็ได้
Ashok kumar

2
@Cuong Le - ถ้าเซลล์มีเครื่องหมายจุลภาคสองตัวมันจะมีปัญหาที่ "string.Join (", ")"
suneel ranga

@Cuong Le สถานที่จะ"excel.csv"อยู่ที่ไหน?
Jogi

2
@suneelranga - ถ้าเซลล์ (เช่นในแถว ItemArray) มี,(ลูกน้ำ) ตามมาตรฐาน CSV เซลล์ควรจะถูกแทนที่ด้วยเครื่องหมายคำพูด","จากนั้นจึงปรากฏในไฟล์ตามปกติ ใช่ - มันจะทำให้เกิดปัญหาเนื่องจากรหัสนี้ตรวจไม่พบ,และใช้เครื่องหมายคำพูด
Tom Leys

1
@ Si8 เมื่อบันทึกแล้วคุณสามารถทำ Process เริ่มต้น (ไฟล์ของคุณ) และจะเปิดขึ้นมาให้ ที่ใกล้ที่สุดเท่าที่จะทำได้ฉันเชื่อ ..
TimmRH

40

ตัวเลือกที่สวยงามคือการเขียนวิธีการขยาย (ดูด้านล่าง) สำหรับคลาส DataTable ของ. net framework

วิธีการขยายนี้สามารถเรียกได้ดังนี้:

using System;
using System.Collections.Generic;
using System.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using System.Data;
using System.Data.OleDb;

DataTable dt;
// fill table data in dt here 
...

// export DataTable to excel
// save excel file without ever making it visible if filepath is given
// don't save excel file, just make it visible if no filepath is given
dt.ExportToExcel(ExcelFilePath);

วิธีการขยายสำหรับคลาส DataTable:

public static class My_DataTable_Extensions
{

    // Export DataTable into an excel file with field names in the header line
    // - Save excel file without ever making it visible if filepath is given
    // - Don't save excel file, just make it visible if no filepath is given
    public static void ExportToExcel(this DataTable tbl, string excelFilePath = null) {
        try {
            if (tbl == null || tbl.Columns.Count == 0)
                throw new Exception("ExportToExcel: Null or empty input table!\n");

            // load excel, and create a new workbook
            var excelApp = new Excel.Application();
            excelApp.Workbooks.Add();

            // single worksheet
            Excel._Worksheet workSheet = excelApp.ActiveSheet;

            // column headings
            for (var i = 0; i < tbl.Columns.Count; i++) {
                workSheet.Cells[1, i + 1] = tbl.Columns[i].ColumnName;
            }

            // rows
            for (var i = 0; i < tbl.Rows.Count; i++) {
                // to do: format datetime values before printing
                for (var j = 0; j < tbl.Columns.Count; j++) {
                    workSheet.Cells[i + 2, j + 1] = tbl.Rows[i][j];
                }
            }

            // check file path
            if (!string.IsNullOrEmpty(excelFilePath)) {
                try {
                    workSheet.SaveAs(excelFilePath);
                    excelApp.Quit();
                    MessageBox.Show("Excel file saved!");
                }
                catch (Exception ex) {
                    throw new Exception("ExportToExcel: Excel file could not be saved! Check filepath.\n"
                                        + ex.Message);
                }
            } else { // no file path is given
                excelApp.Visible = true;
            }
        }
        catch (Exception ex) {
            throw new Exception("ExportToExcel: \n" + ex.Message);
        }
    }
}

20
โปรดทราบว่าสิ่งนี้จำเป็นต้องติดตั้ง Excel
Banshee

5
ExcelFilePath != null && ExcelFilePath != ""อาจจะเป็น!String.IsNullOrEmpty(ExcelFilePath)
Mark Schultheiss

2
หมายเหตุอีกประการหนึ่ง: Microsoft ไม่แนะนำให้ใช้ Interop บนเซิร์ฟเวอร์support.microsoft.com/en-us/help/257757/…
alex.pulver

@ alex.pulver มันก็ใช้ไม่ได้เช่นกันเมื่อฉันพยายามใช้บนเซิร์ฟเวอร์เช่นกัน จุดที่ดีที่จะกล่าวถึง
Si8

วิธีนี้จะได้ผล แต่ช้า คัดลอกไปยังคลิปบอร์ดแล้ววางลงใน excel ได้ดีที่สุด หากคุณทำงานกับระเบียนมากกว่า 1,000 รายการจะใช้เวลาสักครู่
Alex M

25

วิธีแก้ปัญหาตามบทความ tuncalik (ขอบคุณสำหรับแนวคิด) แต่ในกรณีของตารางขนาดใหญ่จะทำงานได้เร็วกว่ามาก (และชัดเจนน้อยกว่าเล็กน้อย)

public static class My_DataTable_Extensions
{
    /// <summary>
    /// Export DataTable to Excel file
    /// </summary>
    /// <param name="DataTable">Source DataTable</param>
    /// <param name="ExcelFilePath">Path to result file name</param>
    public static void ExportToExcel(this System.Data.DataTable DataTable, string ExcelFilePath = null)
    {
        try
        {
            int ColumnsCount;

            if (DataTable == null || (ColumnsCount = DataTable.Columns.Count) == 0)
                throw new Exception("ExportToExcel: Null or empty input table!\n");

            // load excel, and create a new workbook
            Microsoft.Office.Interop.Excel.Application Excel = new Microsoft.Office.Interop.Excel.Application();
            Excel.Workbooks.Add();

            // single worksheet
            Microsoft.Office.Interop.Excel._Worksheet Worksheet = Excel.ActiveSheet;

            object[] Header = new object[ColumnsCount];

            // column headings               
            for (int i = 0; i < ColumnsCount; i++)
                Header[i] = DataTable.Columns[i].ColumnName;

            Microsoft.Office.Interop.Excel.Range HeaderRange = Worksheet.get_Range((Microsoft.Office.Interop.Excel.Range)(Worksheet.Cells[1, 1]), (Microsoft.Office.Interop.Excel.Range)(Worksheet.Cells[1, ColumnsCount]));
            HeaderRange.Value = Header;
            HeaderRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
            HeaderRange.Font.Bold = true;

            // DataCells
            int RowsCount = DataTable.Rows.Count;
            object[,] Cells = new object[RowsCount, ColumnsCount];

            for (int j = 0; j < RowsCount; j++)
                for (int i = 0; i < ColumnsCount; i++)
                    Cells[j, i] = DataTable.Rows[j][i];

            Worksheet.get_Range((Microsoft.Office.Interop.Excel.Range)(Worksheet.Cells[2, 1]), (Microsoft.Office.Interop.Excel.Range)(Worksheet.Cells[RowsCount + 1, ColumnsCount])).Value = Cells;

            // check fielpath
            if (ExcelFilePath != null && ExcelFilePath != "")
            {
                try
                {
                    Worksheet.SaveAs(ExcelFilePath);
                    Excel.Quit();
                    System.Windows.MessageBox.Show("Excel file saved!");
                }
                catch (Exception ex)
                {
                    throw new Exception("ExportToExcel: Excel file could not be saved! Check filepath.\n"
                        + ex.Message);
                }
            }
            else    // no filepath is given
            {
                Excel.Visible = true;
            }
        }
        catch (Exception ex)
        {
            throw new Exception("ExportToExcel: \n" + ex.Message);
        }
    }
}

คำตอบของ tuncalik ใช้เวลาเกือบนาทีสำหรับฉันนี่คือ 1 วินาทีถ้าใช้เวลานาน ... ฉันสะดุ้งจริงๆ
Wilsu

2
นี่เป็นตัวอย่างที่เร็วที่สุดที่ฉันได้ลองเยี่ยมมาก ฉันต้องใช้ Marshal เพื่อปล่อยไฟล์หลังจากนั้น Excel.Quit(); Marshal.FinalReleaseComObject(Worksheet); Marshal.FinalReleaseComObject(HeaderRange); Marshal.FinalReleaseComObject(Excel);
Dave Kelly

จำเป็นต้องติดตั้ง Office หรือไม่
Parshuram Kalvikatte

ทำงานได้อย่างสมบูรณ์แบบ แต่สีพื้น Header Back ของฉันถูกตั้งค่าเป็นสีดำเสมอในขณะที่ใช้โซลูชันนี้ในแอปพลิเคชันคอนโซล อาจเป็นเพราะอะไร ??
Zaveed Abbasi

15

ลองใช้ฟังก์ชันนี้ส่งผ่านเส้นทางข้อมูลและไฟล์ที่คุณต้องการส่งออก

public void CreateCSVFile(ref DataTable dt, string strFilePath)
{            
    try
    {
        // Create the CSV file to which grid data will be exported.
        StreamWriter sw = new StreamWriter(strFilePath, false);
        // First we will write the headers.
        //DataTable dt = m_dsProducts.Tables[0];
        int iColCount = dt.Columns.Count;
        for (int i = 0; i < iColCount; i++)
        {
            sw.Write(dt.Columns[i]);
            if (i < iColCount - 1)
            {
                sw.Write(",");
            }
        }
        sw.Write(sw.NewLine);

        // Now write all the rows.

        foreach (DataRow dr in dt.Rows)
        {
            for (int i = 0; i < iColCount; i++)
            {
                if (!Convert.IsDBNull(dr[i]))
                {
                    sw.Write(dr[i].ToString());
                }
                if (i < iColCount - 1)
                {
                    sw.Write(",");
                }
            }

            sw.Write(sw.NewLine);
        }
        sw.Close();
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

1
โปรดทราบว่าสิ่งนี้จะไม่ใช้เซลล์ตารางในเอกสาร Excel จริง ๆ แต่ทุกอย่างสำหรับแต่ละแถวจะถูกพิมพ์ในเซลล์แรกของทุกแถว
Banshee

@Banshee ไม่ Excel เข้าใจไฟล์ CSV อย่างเต็มที่
NickG

ใช้ไม่ได้กับ excel ของฉันเช่นกัน ข้อมูลของแต่ละแถวอยู่ในเซลล์แรก
Mitulátbáti

5

วิธีที่ดีที่สุดและง่ายที่สุด

private void exportToExcel(DataTable dt)
    {

        /*Set up work book, work sheets, and excel application*/
        Microsoft.Office.Interop.Excel.Application oexcel = new Microsoft.Office.Interop.Excel.Application();
        try
        {
            string path = AppDomain.CurrentDomain.BaseDirectory;
            object misValue = System.Reflection.Missing.Value;
            Microsoft.Office.Interop.Excel.Workbook obook = oexcel.Workbooks.Add(misValue);
            Microsoft.Office.Interop.Excel.Worksheet osheet = new Microsoft.Office.Interop.Excel.Worksheet();


          //  obook.Worksheets.Add(misValue);

            osheet = (Microsoft.Office.Interop.Excel.Worksheet)obook.Sheets["Sheet1"];
            int colIndex = 0;
            int rowIndex = 1;

            foreach (DataColumn dc in dt.Columns)
            {
                colIndex++;
                osheet.Cells[1, colIndex] = dc.ColumnName;
            }
            foreach (DataRow dr in dt.Rows)
            {
                rowIndex++;
                colIndex = 0;

                foreach (DataColumn dc in dt.Columns)
                {
                    colIndex++;
                    osheet.Cells[rowIndex, colIndex] = dr[dc.ColumnName];
                }
            }

            osheet.Columns.AutoFit();
            string filepath = "C:\\Temp\\Book1";

            //Release and terminate excel

            obook.SaveAs(filepath);
            obook.Close();
            oexcel.Quit();
            releaseObject(osheet);

            releaseObject(obook);

            releaseObject(oexcel);
            GC.Collect();
        }
        catch (Exception ex)
        {
            oexcel.Quit();
            log.AddToErrorLog(ex, this.Name);
        }
    }

1
private void releaseObject(object o) { try { while (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0) { } } catch { } finally { o = null; } }
ทิม

(โปรดอยู่ที่นี่) หากมีไฟล์ excel ที่เปิดอยู่มากกว่าหนึ่งไฟล์ฟังก์ชันรีลีสนี้จะทำลายไฟล์ทั้งหมดหรือไฟล์ที่ส่งผ่านเป็นพารามิเตอร์เท่านั้น?
Elliott Addi

1

การทำงานร่วมกันของ Excel:

วิธีนี้จะป้องกันไม่ให้ Dates พลิกจาก dd-mm-yyyy เป็น mm-dd-yyyy

public bool DataTableToExcelFile(DataTable dt, string targetFile)
{
    const bool dontSave = false;
    bool success = true;

    //Exit if there is no rows to export
    if (dt.Rows.Count == 0) return false;

    object misValue = System.Reflection.Missing.Value;
    List<int> dateColIndex = new List<int>();
    Excel.Application excelApp = new Excel.Application();
    Excel.Workbook excelWorkBook = excelApp.Workbooks.Add(misValue);
    Excel.Worksheet excelWorkSheet = excelWorkBook.Sheets("sheet1");

    //Iterate through the DataTable and populate the Excel work sheet
    try {
        for (int i = -1; i <= dt.Rows.Count - 1; i++) {
            for (int j = 0; j <= dt.Columns.Count - 1; j++) {
                if (i < 0) {
                    //Take special care with Date columns
                    if (dt.Columns(j).DataType is typeof(DateTime)) {
                        excelWorkSheet.Cells(1, j + 1).EntireColumn.NumberFormat = "d-MMM-yyyy;@";
                        dateColIndex.Add(j);
                    } 
                    //else if ... Feel free to add more Formats

                    else {
                        //Otherwise Format the column as text
                        excelWorkSheet.Cells(1, j + 1).EntireColumn.NumberFormat = "@";
                    }
                    excelWorkSheet.Cells(1, j + 1) = dt.Columns(j).Caption;
                } 
                else if (dateColIndex.IndexOf(j) > -1) {
                    excelWorkSheet.Cells(i + 2, j + 1) = Convert.ToDateTime(dt.Rows(i).ItemArray(j)).ToString("d-MMM-yyyy");
                } 
                else {
                    excelWorkSheet.Cells(i + 2, j + 1) = dt.Rows(i).ItemArray(j).ToString();
                }
            }
        }

        //Add Autofilters to the Excel work sheet  
        excelWorkSheet.Cells.AutoFilter(1, Type.Missing, Excel.XlAutoFilterOperator.xlAnd, Type.Missing, true);
        //Autofit columns for neatness
        excelWorkSheet.Columns.AutoFit();
        if (File.Exists(exportFile)) File.Delete(exportFile);
        excelWorkSheet.SaveAs(exportFile);
    } catch {
        success = false;
    } finally {
        //Do this irrespective of whether there was an exception or not. 
        excelWorkBook.Close(dontSave);
        excelApp.Quit();
        releaseObject(excelWorkSheet);
        releaseObject(excelWorkBook);
        releaseObject(excelApp);
    }
    return success;
}

หากคุณไม่สนใจเกี่ยวกับการพลิกวันที่ให้ใช้ลิงก์ดูที่แสดงวิธีเติมข้อมูลเซลล์ทั้งหมดในสเปรดชีต Excel ในโค้ดบรรทัดเดียว:

Excel Interop - ประสิทธิภาพและประสิทธิภาพ

CSV:

public string DataTableToCSV(DataTable dt, bool includeHeader, string rowFilter, string sortFilter, bool useCommaDelimiter = false, bool truncateTimesFromDates = false)
{
    dt.DefaultView.RowFilter = rowFilter;
    dt.DefaultView.Sort = sortFilter;
    DataView dv = dt.DefaultView;
    string csv = DataTableToCSV(dv.ToTable, includeHeader, useCommaDelimiter, truncateTimesFromDates);
    //reset the Filtering
    dt.DefaultView.RowFilter = string.Empty;
    return csv;
}

public string DataTableToCsv(DataTable dt, bool includeHeader, bool useCommaDelimiter = false, bool truncateTimesFromDates = false)
{
    StringBuilder sb = new StringBuilder();
    string delimter = Constants.vbTab;
    if (useCommaDelimiter)
        delimter = ",";

    if (includeHeader) {
        foreach (DataColumn dc in dt.Columns) {
            sb.AppendFormat("{0}" + Constants.vbTab, dc.ColumnName);
        }

        //remove the last Tab
        sb.Remove(sb.ToString.Length - 1, 1);
        sb.Append(Environment.NewLine);
    }

    foreach (DataRow dr in dt.Rows) {
        foreach (DataColumn dc in dt.Columns) {
            if (Information.IsDate(dr(dc.ColumnName).ToString()) & dr(dc.ColumnName).ToString().Contains(".") == false & truncateTimesFromDates) {
                sb.AppendFormat("{0}" + delimter, Convert.ToDateTime(dr(dc.ColumnName).ToString()).Date.ToShortDateString());
            } else {
                sb.AppendFormat("{0}" + delimter, CheckDBNull(dr(dc.ColumnName).ToString().Replace(",", "")));
            }
        }
        //remove the last Tab
        sb.Remove(sb.ToString.Length - 1, 1);
        sb.Append(Environment.NewLine);
    }
    return sb.ToString;
}

public enum enumObjectType
{
    StrType = 0,
    IntType = 1,
    DblType = 2
}

public object CheckDBNull(object obj, enumObjectType ObjectType = enumObjectType.StrType)
{
    object objReturn = null;
    objReturn = obj;
    if (ObjectType == enumObjectType.StrType & Information.IsDBNull(obj)) {
        objReturn = "";
    } else if (ObjectType == enumObjectType.IntType & Information.IsDBNull(obj)) {
        objReturn = 0;
    } else if (ObjectType == enumObjectType.DblType & Information.IsDBNull(obj)) {
        objReturn = 0.0;
    }
    return objReturn;
}

1

เพียงแค่ใช้ไลบรารีCloseMXL.Excel มันง่ายและค่อนข้างเร็วด้วย

คลาส

private DataTable getAllList()
        {
            string constr = ConfigurationManager.ConnectionStrings["RConnection"].ConnectionString;
            using (SqlConnection con = new SqlConnection(constr))
            {
                using (SqlCommand cmd = new SqlCommand("SELECT EmpId, gender, EmpName, pOnHold FROM Employee  WHERE EmpId= '"+ AnyVariable + "' ORDER BY EmpName"))
                {
                    using (SqlDataAdapter da = new SqlDataAdapter())
                    {
                        DataTable dt = new DataTable();
                        cmd.CommandType = CommandType.Text;
                        cmd.Connection = con;
                        da.SelectCommand = cmd;
                        da.Fill(dt);
                        dt.Columns[0].ColumnName = "Employee Id";
                        dt.Columns[1].ColumnName = "Gender";
                        dt.Columns[2].ColumnName = "Employee Name";
                        dt.Columns[3].ColumnName = "On Hold";

                        return dt;
                    }
                }
            }
        }

จากนั้นอีกวิธีหนึ่งที่ได้รับชุดข้อมูล

public DataSet getDataSetExportToExcel()
        {
            DataSet ds = new DataSet();
            DataTable dtEmp = new DataTable("CLOT List");
            dtEmp = getAllList();
             ds.Tables.Add(dtEmp);
             ds.Tables[0].TableName = "Employee"; //If you which to use Mutliple Tabs
             return ds;
          }

ตอนนี้คุณปุ่มคลิกเหตุการณ์

protected void btn_Export_Click(object sender, EventArgs e)
        {
            DataSet ds = getDataSetExportToExcel();

            using (XLWorkbook wb = new XLWorkbook())
            {
                wb.Worksheets.Add(ds);
                wb.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
                wb.Style.Font.Bold = true;

                Response.Clear();
                Response.Buffer = true;
                Response.Charset = "";
                Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                Response.AddHeader("content-disposition", "attachment;filename=EmployeeonHoldList.xlsx");

                using (MemoryStream MyMemoryStream = new MemoryStream())
                {
                    wb.SaveAs(MyMemoryStream);
                    MyMemoryStream.WriteTo(Response.OutputStream);

                    Response.Flush();
                    Response.End();
                }
            }
        }

1

คุณสามารถใช้ไลบรารีSwiftExcelของฉัน โดยเฉพาะอย่างยิ่งเมื่อมีประสิทธิภาพและการใช้หน่วยความจำต่ำเนื่องจากเขียนข้อมูลลงในไฟล์โดยตรง:

using (var ew = new ExcelWriter("C:\\temp\\test.xlsx"))
{
    for (var row = 1; row <= 100; row++)
    {
        for (var col = 1; col <= 10; col++)
        {
            ew.Write($"row:{row}-col:{col}", col, row);
        }
    }
}

คำสั่ง Nuget เพื่อติดตั้ง:

Install-Package SwiftExcel

1

ฉันต้องการเพิ่มคำตอบนี้เนื่องจากฉันใช้เวลาส่วนใหญ่ในการค้นหาวิธีการที่รวดเร็วและเชื่อถือได้ในการทำสิ่งนี้และไม่มีตัวอย่างทั้งหมดของการใช้ OpenXMLWriter เพื่อจุดประสงค์นี้ในทุกที่ที่ฉันสามารถหาได้

ประการแรก COM / Interop (ซึ่งคำตอบอื่น ๆ หลายคำใช้) ใช้ได้สำหรับจุดประสงค์นี้ แต่ก็มีความไวบางประการ ฉันใช้มาหลายสิบปีแล้วและส่วนใหญ่มีความเสถียร แต่เมื่อใช้งานส่วนหน้าของคลังข้อมูลสำหรับผู้ใช้หลายร้อยคนฉันพบว่ามีปัญหามากเกินไปขึ้นอยู่กับเครื่องและสิ่งที่ผู้ใช้ทำดังนั้นฉันจึงเปลี่ยนไปใช้ OpenXML OpenXML DOM ค่อนข้างดีสำหรับจุดประสงค์นี้ แต่ช้ากว่าการใช้ OpenXMLWriter เมื่อคุณเข้าสู่ชุดข้อมูลขนาดใหญ่ (100K +) ที่มีคอลัมน์จำนวนมาก DOM จะช้ากว่า OpenXMLWriter มากดังนั้นฉันจึงใช้ชุดหลัง วิธีการด้านล่างเขียน 420K + แถวที่มี 30+ ฟิลด์ในเวลาน้อยกว่า 30 วินาที

ฉันหวังว่าความคิดเห็นจะเพียงพอที่จะแนะนำทุกคนว่ากำลังทำอะไรอยู่ ทำให้ง่ายขึ้นเนื่องจากเขียนค่าทั้งหมดลงในไฟล์เป็นสตริง แต่คุณสามารถใช้ตรรกะในการเขียนประเภทข้อมูลต่างๆ (และใช้รูปแบบเซลล์ต่างๆ) ตามเนื้อหาของข้อมูลของคุณ คุณยังสามารถปรับสิ่งนี้เพื่อใช้กับ DataGridView (แทนที่จะเป็น DataTable) โดยเปลี่ยนเพียงไม่กี่อย่าง (คือการวนซ้ำผ่านคอลัมน์ / แถว)

จำเป็นต้องมีการอ้างอิงถึง DocumentFormat.OpenXML (d / l พร้อม OpenXML SDK) และ WindowsBase

Imports DocumentFormat.OpenXml
Imports DocumentFormat.OpenXml.Spreadsheet
Imports DocumentFormat.OpenXml.Packaging

Public Sub ExportToExcelXML(ByRef dt As DataTable, filename As String)
    Dim wbp As WorkbookPart, wsp As WorksheetPart
    'If this DataTable has more rows in it than can fit in Excel, throw an exception
    If dt.Rows.Count > 1048575 Then Throw New Exception("The DataTable is too large to export to Excel.")
    'Delete any previous file of the same name that may exist.
    File.Delete(filename)
    'Create an OpenXML SpreadsheetDocument...
    Using xls = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook)
        'Add a WorkbookPart to the Spreadsheet Doc, then add a WorksheetPart to the WorkbookPart.
        wbp = xls.AddWorkbookPart()
        wsp = wbp.AddNewPart(Of WorksheetPart)
        'Now we need to add the "StyleSheet" to the WorkbookPart (that we just added above). This will allow us to apply formatting to our Cells.
        'Add the WbStylesPart and the StyleSheet.
        Dim stp As WorkbookStylesPart = wbp.AddNewPart(Of WorkbookStylesPart)
        Dim ss As New Stylesheet
        'Create the only two Fonts we're going to use (Regular and Bold).
        Dim fBold As New Font
        fBold.Append(New Bold)
        Dim fnts As New Fonts
        fnts.Append(New Font) 'This creates the default (unmodified, regular) Font. It's added first, so its index is 0.
        fnts.Append(fBold) 'This creates the Bold font. It's added second, so its index is 1.
        'Create the default Fill/Border settings (these have to be here, even though I don't set any custom fills/borders).
        Dim flls As New Fills
        Dim brdrs As New Borders
        flls.Append(New Fill)
        brdrs.Append(New Border)
        'Now I have to add formats (NumberFormat and CellFormat). First, you create a NumberFormat. This is basically the pattern of 
        '   the format (i.e. "@" for Text). For now, I only need a Text format, but I can add more patterns if needed.
        '   I give the format an ID of 164, since 163 is where the built-in Excel formats end.
        Dim nbrfmts As New NumberingFormats
        nbrfmts.Append(New NumberingFormat With {.NumberFormatId = 164, .FormatCode = "@"})
        'Create the first two CellFormats: Default, which will have an index of 0 and "Header" (Bold/Centered) with an index of 1.
        Dim cellfmts As New CellFormats()
        cellfmts.Append(New CellFormat() With {.FontId = 0, .NumberFormatId = 164, .FillId = 0, .BorderId = 0})
        cellfmts.Append(New CellFormat() With {.FontId = 1, .NumberFormatId = 164,
            .Alignment = New Alignment() With {.WrapText = True, .Horizontal = HorizontalAlignmentValues.Center}})
        'Add all of the Fonts/Fills/Borders/etc to the StyleSheet and add it all to the WorkbookStylesPart.
        ss.Append(fnts)
        ss.Append(flls)
        ss.Append(brdrs)
        ss.Append(cellfmts)
        ss.NumberingFormats = nbrfmts
        stp.Stylesheet = ss
        stp.Stylesheet.Save()
        'Now create an OpenXMLWriter using the WorksheetPart to write the cells to the worksheet.
        Using oxw As OpenXmlWriter = OpenXmlWriter.Create(wsp)
            'Write the start element for the Worksheet and the Columns...
            oxw.WriteStartElement(New Worksheet)
            oxw.WriteStartElement(New Columns())
            'Now I'm going to loop through the columns in the DataTable...
            For c As Integer = 0 To dt.Columns.Count - 1
                'Now we'll get the width for the column. To do this, we loop through all of the rows and measure the width of the text 
                '   using the default Excel Font (currently Font: Calibri Size: 11) and return the largest width (in pixels) to use below.
                '   Why not do this loop below (when I loop through the rows to write the Cells)? Because you can't. You have to
                '   write the Column XML first before writing the SheetData/Row/Cell XML (I confirmed this by trying it), so there's
                '   no way (that I'm aware of) to avoid looping through all of the rows twice if you want to AutoFit.
                'Setup vars we'll use for getting the column widths (below).
                Dim g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero)
                Dim fnt = New System.Drawing.Font("Calibri", 11)
                Dim wid As Double = 0
                'Get the width of the header (because if this is wider than the widest value, we'll use the header text's width).
                '   I found that adding 2 pixels to the width was necessary to get the column as wide as Excel would make it.
                Dim tmp As Double = g.MeasureString(dt.Columns(c).ColumnName, New System.Drawing.Font(fnt, System.Drawing.FontStyle.Bold)).Width + 2
                'Loop through the rows in the dt and get the width of the value in that row/col. If it's wider than the widest
                '   width we've encountered thus far, use the new wider width as our basis.
                For Each row As DataRow In dt.Rows
                    If tmp > wid Then wid = tmp
                    tmp = g.MeasureString(row(c).ToString, fnt).Width
                Next
                'Set the column attributes and write it to the file. The Width is set using a formula that converts from pixels to Excel's column width values.
                Dim oxa As New List(Of OpenXmlAttribute) From {New OpenXmlAttribute("min", Nothing, c + 1), New OpenXmlAttribute("max", Nothing, c + 1),
                    New OpenXmlAttribute("width", Nothing, System.Math.Round((wid - 12 + 5) / 7D + 1, 2))}
                oxw.WriteStartElement(New Column(), oxa)
                oxw.WriteEndElement()
            Next
            'CLose out the Columns collection.
            oxw.WriteEndElement()
            'Write the start element for the SheetData...
            oxw.WriteStartElement(New SheetData)
            'Write the start element for the Header row.
            oxw.WriteStartElement(New Row)
            'Loop through the Columns in the dt.
            For Each col As DataColumn In dt.Columns
                'Write a cell for this column's Header. All Header cells are written with a DataType of String ("str"). 
                '   I ALSO apply the "Header" CellFormat (StyleIndex 1) to all of the Header Cells. This makes them Bold and Centered.
                WriteCell(oxw, col.ColumnName, "str", 1)
            Next
            'Close out the Header row.
            oxw.WriteEndElement()
            'Loop through all of the rows in the dt...
            For Each row As DataRow In dt.Rows
                'Write a StartElement for this row...
                oxw.WriteStartElement(New Row)
                'Loop through all of the columns in the dt...
                For c As Integer = 0 To dt.Columns.Count - 1
                    'Write a value in this row/column to the Excel file. I use the datatype of "String" and the default CellFormat/StyleIndex.
                    WriteCell(oxw, row(c).ToString, "str", 0)
                Next
                'Close out this row.
                oxw.WriteEndElement()
            Next
            'Close out the Worksheet and SheetData elements...
            oxw.WriteEndElement()
            oxw.WriteEndElement()
        End Using
        'Now we're going to create an OpenXMLWriter using the WorkbookPart (that we created above)...
        Using oxw As OpenXmlWriter = OpenXmlWriter.Create(wbp)
            'Add starting elements for the Workbook and Sheets collection.
            oxw.WriteStartElement(New Workbook())
            oxw.WriteStartElement(New Sheets())
            'Add the Sheet (name the Sheet after the file name minus the extension).
            oxw.WriteElement(New Sheet() With {.Name = Path.GetFileNameWithoutExtension(filename), .SheetId = 1, .Id = xls.WorkbookPart.GetIdOfPart(wsp)})
            'Write End elements for the Workbook/Sheets
            oxw.WriteEndElement()
            oxw.WriteEndElement()
        End Using
    End Using

End Sub

'This Sub is used to write a value to a Cell using OpenXMLWriter.
Private Sub WriteCell(ByRef oxw As OpenXmlWriter, value As String, datatype As String, style As UInt32Value)
    Dim oxa As New List(Of OpenXmlAttribute) From {New OpenXmlAttribute("t", Nothing, datatype), New OpenXmlAttribute("s", Nothing, style)}
    oxw.WriteStartElement(New Cell(), oxa)
    If value <> Nothing Then oxw.WriteElement(New CellValue(value))
    oxw.WriteEndElement()
End Sub

1
ขอบคุณมากที่สละเวลาในคำตอบนี้ ฉันมีลูกค้าที่มีโซลูชันการทำงานใน Excel Interop แต่บ่นว่าทำงานช้าแค่ไหน ฉันเห็นคำตอบอื่น ๆ อีกสองสามข้อเกี่ยวกับคำถามที่นำฉันไปสู่ ​​OpenXML แต่ฉันดีใจที่มีทางลัดในการเริ่มต้นใช้งาน
Brandon Barkley

1
ไม่มีปัญหา. ฉันยังคงใช้ COM แต่เฉพาะในสภาพแวดล้อมที่ฉันสามารถควบคุมได้ทั้งหมด ฉันใช้แนวทาง OpenXML นี้ในแอปที่มีผู้ใช้สองร้อยคนเป็นเวลาสองสามเดือนแล้วและไม่มีปัญหาใด ๆ เมื่อเทียบกับข้อผิดพลาดรายสัปดาห์กับ COM ฉันดูโซลูชันของบุคคลที่สามเช่นกันเช่นเดียวกับที่กล่าวถึงที่นี่ แต่ฉันชอบเขียนด้วยตัวเองเพื่อให้มีประสิทธิภาพมากที่สุด
WATYF

0

เกี่ยวกับคำตอบของtuncalikซึ่งดีมากโดยเฉพาะอย่างยิ่งถ้าคุณต้องการเล่นกับโค้ดเล็กน้อย :) แต่จะใส่วันที่ของฉันลงใน Excel ในรูปแบบอเมริกันเช่น 2 มีนาคม 2014 ในสหราชอาณาจักรคือ 02/03/2014 แต่ ในสหรัฐอเมริกาเมื่อวันที่ 02/02/2014 กับเดือนที่ 1 จากนั้นวันของสัปดาห์หลังจากนั้น ฉันจำเป็นต้องมีในรูปแบบสหราชอาณาจักรโปรดมีความคิดอย่างไร

ฉันได้ตรวจสอบแล้วว่ามันถูกเก็บไว้ในรูปแบบสหราชอาณาจักรใน DataTable ของฉันและ Excel ของฉันก็ตั้งค่าเป็นสหราชอาณาจักร แต่ด้วยเหตุผลบางประการเมื่อมันสร้างเอกสาร Excel ก็คิดว่าเป็นของสหรัฐอเมริกา (นี่เป็นเพราะ Microsoft เป็น บริษัท ในสหรัฐอเมริกาหรือไม่ :)

ฉันจะลองทดสอบรหัสวัฒนธรรม แต่ยังไม่แน่ใจว่าจะเอาไปไว้ที่ไหน พยายามแล้ว แต่ไม่มีผล

ปล

ฉันต้องเปลี่ยนหนึ่งบรรทัดเพื่อให้มันใช้งานได้โดยเพิ่ม 'นักแสดง' ตามด้านล่าง

// single worksheet
Excel._Worksheet workSheet = (Excel._Worksheet)excelApp.ActiveSheet;

อัปเดต: ฉันประสบความสำเร็จในการจัดรูปแบบวันที่ในสหราชอาณาจักรโดยการแปลงเป็นรูปแบบ LongDateTime ซึ่งเป็นเพียงวิธีแก้ปัญหาเท่านั้น

DateTime startDate = Convert.ToDateTime(myList[0].ToString());
string strStartDate = startDate.ToLongDateString();
DateTime endDate = Convert.ToDateTime(myList[myListTotalRows].ToString());
string strEndDate = endDate.ToLongDateString();    

ไชโย


0

คุณสามารถใช้EasyXLSซึ่งเป็นไลบรารีสำหรับส่งออกไฟล์ Excel

ตรวจสอบรหัสนี้:

DataSet ds = new DataSet();
ds.Tables.Add(dataTable);

ExcelDocument xls = new ExcelDocument();
xls.easy_WriteXLSFile_FromDataSet("datatable.xls", ds, 
           new ExcelAutoFormat(Styles.AUTOFORMAT_EASYXLS1), "DataTable");

ดูเพิ่มเติมตัวอย่างนี้เกี่ยวกับวิธีการส่งออกไปยัง DataTable Excel ใน C #


0

กระทู้เก่า - แต่คิดว่าฉันจะโยนรหัสของฉันที่นี่ ฉันเขียนฟังก์ชันเล็กน้อยเพื่อเขียนตารางข้อมูลไปยังแผ่นงาน excel ใหม่ตามเส้นทางที่ระบุ (ตำแหน่ง) นอกจากนี้คุณจะต้องเพิ่มการอ้างอิงไปยังไลบรารี microsoft excel 14.0

ฉันดึงจากหัวข้อนี้เกี่ยวกับการเขียนอะไรก็ได้เพื่อเก่ง - วิธีเขียนข้อมูลลงในไฟล์ excel (.xlsx)

ฉันใช้สิ่งนั้นในการคาดคะเนวิธีการเขียน datatable

* หมายเหตุในคำสั่ง catch ฉันมีการอ้างอิงคลาสแบบคงที่ตัวจัดการข้อผิดพลาด (คุณสามารถเพิกเฉยต่อสิ่งเหล่านั้นได้)

 using excel = Microsoft.Office.Interop.Excel;
 using System.IO;
 using System.Data;
 using System.Runtime.InteropServices;

 //class and namespace wrapper is not shown in this example 

 private void WriteToExcel(System.Data.DataTable dt, string location)
    {
        //instantiate excel objects (application, workbook, worksheets)
        excel.Application XlObj = new excel.Application();
        XlObj.Visible = false;
        excel._Workbook WbObj = (excel.Workbook)(XlObj.Workbooks.Add(""));
        excel._Worksheet WsObj = (excel.Worksheet)WbObj.ActiveSheet;

        //run through datatable and assign cells to values of datatable
        try
        {
            int row = 1; int col = 1;
            foreach (DataColumn column in dt.Columns)
            {
                //adding columns
                WsObj.Cells[row, col] = column.ColumnName;
                col++;
            }
            //reset column and row variables
            col = 1;
            row++;
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                //adding data
                foreach (var cell in dt.Rows[i].ItemArray)
                {
                    WsObj.Cells[row, col] = cell;
                    col++;
                }
                col = 1;
                row++;
            }
            WbObj.SaveAs(location);
        }
        catch (COMException x)
        {                
            ErrorHandler.Handle(x);
        }
        catch (Exception ex)
        {               
            ErrorHandler.Handle(ex);
        }
        finally
        {
            WbObj.Close();                
        }
    }

1
อย่างไรก็ตามวิธีนี้ใช้งานได้ดีคุณจะไม่ฆ่ากระบวนการ Excel ของคุณในภายหลังดังนั้นฉันขอแนะนำให้เพิ่มสิ่งนี้แทนที่SaveAsบรรทัดของคุณตามที่รวมไว้ที่นี่: 'XlObj.DisplayAlerts = false; WbObj.SaveAs (สถานที่); WbObj.Close (); XlObj.Quit (); Marshal.ReleaseComObject (WsObj); Marshal.ReleaseComObject (WbObj); Marshal.ReleaseComObject (XlObj); ' หากต้องการใช้Marshal.ReleaseComObjectวิธีนี้ให้เพิ่มusing System.Runtime.InteropServicesแอสเซมบลีในโครงการของคุณ
GrammatonCleric

0

วิธีหนึ่งในการทำเช่นนี้ก็คือACE OLEDB Provider (ดูสตริงการเชื่อมต่อสำหรับ Excel ด้วย ) แน่นอนว่าคุณต้องติดตั้งและลงทะเบียนผู้ให้บริการ คุณควรมีหากคุณติดตั้ง Excel ไว้ แต่นี่คือสิ่งที่คุณต้องพิจารณาเมื่อปรับใช้แอป

นี่คือตัวอย่างของการเรียกวิธีการช่วยเหลือจากExportHelper:ExportHelper.CreateXlsFromDataTable(myDataTable, @"C:\tmp\export.xls");

ตัวช่วยในการส่งออกไปยังไฟล์ Excel โดยใช้ ACE OLEDB:

public class ExportHelper
{
    private const string ExcelOleDbConnectionStringTemplate = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 8.0;HDR=YES\";";

    /// <summary>
    /// Creates the Excel file from items in DataTable and writes them to specified output file.
    /// </summary>
    public static void CreateXlsFromDataTable(DataTable dataTable, string fullFilePath)
    {
        string createTableWithHeaderScript = GenerateCreateTableCommand(dataTable);

        using (var conn = new OleDbConnection(String.Format(ExcelOleDbConnectionStringTemplate, fullFilePath)))
        {
            if (conn.State != ConnectionState.Open)
            {
                conn.Open();
            }

            OleDbCommand cmd = new OleDbCommand(createTableWithHeaderScript, conn);
            cmd.ExecuteNonQuery();

            foreach (DataRow dataExportRow in dataTable.Rows)
            {
                AddNewRow(conn, dataExportRow);
            }
        }
    }

    private static void AddNewRow(OleDbConnection conn, DataRow dataRow)
    {
        string insertCmd = GenerateInsertRowCommand(dataRow);

        using (OleDbCommand cmd = new OleDbCommand(insertCmd, conn))
        {
            AddParametersWithValue(cmd, dataRow);
            cmd.ExecuteNonQuery();
        }
    }

    /// <summary>
    /// Generates the insert row command.
    /// </summary>
    private static string GenerateInsertRowCommand(DataRow dataRow)
    {
        var stringBuilder = new StringBuilder();
        var columns = dataRow.Table.Columns.Cast<DataColumn>().ToList();
        var columnNamesCommaSeparated = string.Join(",", columns.Select(x => x.Caption));
        var questionmarkCommaSeparated = string.Join(",", columns.Select(x => "?"));

        stringBuilder.AppendFormat("INSERT INTO [{0}] (", dataRow.Table.TableName);
        stringBuilder.Append(columnNamesCommaSeparated);
        stringBuilder.Append(") VALUES(");
        stringBuilder.Append(questionmarkCommaSeparated);
        stringBuilder.Append(")");
        return stringBuilder.ToString();
    }

    /// <summary>
    /// Adds the parameters with value.
    /// </summary>
    private static void AddParametersWithValue(OleDbCommand cmd, DataRow dataRow)
    {
        var paramNumber = 1;

        for (int i = 0; i <= dataRow.Table.Columns.Count - 1; i++)
        {
            if (!ReferenceEquals(dataRow.Table.Columns[i].DataType, typeof(int)) && !ReferenceEquals(dataRow.Table.Columns[i].DataType, typeof(decimal)))
            {
                cmd.Parameters.AddWithValue("@p" + paramNumber, dataRow[i].ToString().Replace("'", "''"));
            }
            else
            {
                object value = GetParameterValue(dataRow[i]);
                OleDbParameter parameter = cmd.Parameters.AddWithValue("@p" + paramNumber, value);
                if (value is decimal)
                {
                    parameter.OleDbType = OleDbType.Currency;
                }
            }

            paramNumber = paramNumber + 1;
        }
    }

    /// <summary>
    /// Gets the formatted value for the OleDbParameter.
    /// </summary>
    private static object GetParameterValue(object value)
    {
        if (value is string)
        {
            return value.ToString().Replace("'", "''");
        }
        return value;
    }

    private static string GenerateCreateTableCommand(DataTable tableDefination)
    {
        StringBuilder stringBuilder = new StringBuilder();
        bool firstcol = true;

        stringBuilder.AppendFormat("CREATE TABLE [{0}] (", tableDefination.TableName);

        foreach (DataColumn tableColumn in tableDefination.Columns)
        {
            if (!firstcol)
            {
                stringBuilder.Append(", ");
            }
            firstcol = false;

            string columnDataType = "CHAR(255)";

            switch (tableColumn.DataType.Name)
            {
                case "String":
                    columnDataType = "CHAR(255)";
                    break;
                case "Int32":
                    columnDataType = "INTEGER";
                    break;
                case "Decimal":
                    // Use currency instead of decimal because of bug described at 
                    // http://social.msdn.microsoft.com/Forums/vstudio/en-US/5d6248a5-ef00-4f46-be9d-853207656bcc/localization-trouble-with-oledbparameter-and-decimal?forum=csharpgeneral
                    columnDataType = "CURRENCY";
                    break;
            }

            stringBuilder.AppendFormat("{0} {1}", tableColumn.ColumnName, columnDataType);
        }
        stringBuilder.Append(")");

        return stringBuilder.ToString();
    }
}

0

ใช้คลาสต่อไปนี้

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using excel = Microsoft.Office.Interop.Excel;
using EL = ExcelLibrary.SpreadSheet;
using System.Drawing;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Forms;


namespace _basic
{
public class ExcelProcesser
{
    public void WriteToExcel(System.Data.DataTable dt)
    {
        excel.Application XlObj = new excel.Application();
        XlObj.Visible = false;
        excel._Workbook WbObj = (excel.Workbook)(XlObj.Workbooks.Add(""));
        excel._Worksheet WsObj = (excel.Worksheet)WbObj.ActiveSheet;
        object misValue = System.Reflection.Missing.Value;


        try
        {
            int row = 1; int col = 1;
            foreach (DataColumn column in dt.Columns)
            {
                //adding columns
                WsObj.Cells[row, col] = column.ColumnName;
                col++;
            }
            //reset column and row variables
            col = 1;
            row++;
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                //adding data
                foreach (var cell in dt.Rows[i].ItemArray)
                {
                    WsObj.Cells[row, col] = cell;
                    col++;
                }
                col = 1;
                row++;
            }
            WbObj.SaveAs(fileFullName, excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
        finally
        {
            WbObj.Close(true, misValue, misValue);
        }
    }
}

}


0

โดยพื้นฐานแล้วโซลูชันนี้จะList<Object>ส่งข้อมูลไปยัง Excel โดยใช้ DataTable เพื่อให้บรรลุสิ่งนี้ฉันใช้วิธีการขยายดังนั้นโดยทั่วไปมีสองสิ่งที่จำเป็น 1. วิธีการขยาย

public static class ReportHelper
{
    public static string ToExcel<T>(this IList<T> data)
    {
        PropertyDescriptorCollection properties =
            TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        foreach (PropertyDescriptor prop in properties)
        {
            //table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            if (prop.Attributes[typeof(FGMS.Entity.Extensions.ReportHeaderAttribute)] != null)
            {
                table.Columns.Add(GetColumnHeader(prop), Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            }
        }

        //So it seems like when there is only one row of data the headers do not appear
        //so adding a dummy blank row which fixed the issues
        //Add a blank Row - Issue # 1471
        DataRow blankRow = table.NewRow();
        table.Rows.Add(blankRow);

        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
                //row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                if (prop.Attributes[typeof(FGMS.Entity.Extensions.ReportHeaderAttribute)] != null)
                {
                    row[GetColumnHeader(prop)] = prop.GetValue(item) ?? DBNull.Value;
                }
            table.Rows.Add(row);
        }
        table.TableName = "Results";
        var filePath = System.IO.Path.GetTempPath() + "\\" + System.Guid.NewGuid().ToString() + ".xls";
        table.WriteXml(filePath);

        return filePath;
    }

    private static string GetColumnHeader(PropertyDescriptor prop)
    {
        return ((FGMS.Entity.Extensions.ReportHeaderAttribute)(prop.Attributes[typeof(FGMS.Entity.Extensions.ReportHeaderAttribute)])).ReportHeaderText;
    }       
}
  1. ตกแต่งชั้นเรียน DTO ของคุณด้วยแอตทริบิวต์ [ReportHeaderAttribute("Column Name")]
public class UserDTO
    {
        public int Id { get; set; }
        public int SourceId { get; set; }
        public string SourceName { get; set; }

        [ReportHeaderAttribute("User Type")]
        public string UsereType { get; set; }

        [ReportHeaderAttribute("Address")]
        public string Address{ get; set; }

        [ReportHeaderAttribute("Age")]
        public int Age{ get; set; }

        public bool IsActive { get; set; }

        [ReportHeaderAttribute("Active")]
        public string IsActiveString
        {
            get
            {
                return IsActive ? "Yes" : "No";
            }
        }}

ทุกสิ่งที่ต้องเป็นคอลัมน์ใน Excel จะต้องได้รับการตกแต่งด้วย [ReportHeaderAttribute("Column Name")]

จากนั้นก็เพียง

Var userList = Service.GetUsers() //Returns List of UserDTO;
var excelFilePath = userList.ToExcel();

HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
            var stream = new FileStream(excelFilePath, FileMode.Open);
            result.Content = new StreamContent(stream);
            result.Content.Headers.ContentType =
                new MediaTypeHeaderValue("application/vnd.ms-excel");
            result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "UserList.xls" };

            return result;

และถ้าฝ่ายปฏิบัติการไม่ต้องการสร้าง DTO สำหรับทุกตารางพวกเขาจะเรียกใช้สิ่งนี้กับ? เช่นเดียวกับตัวอย่าง foreach ของ 1,000 ตารางของฉันทำสิ่งนี้ การเพิ่มแอตทริบิวต์ส่วนหัวของรายงานไม่ได้เกิดขึ้นทันที - มีการเข้ารหัสจำนวนมากที่นั่นและก่อนที่จะทำงานจริง ไม่ได้เคาะโซลูชันของคุณ - เพียงแค่ชี้ให้เห็นว่าหลักการเฉื่อยชาไม่ได้ถูกนำมาใช้ที่นี่เนื่องจากโซลูชันนี้เพิ่มขั้นตอนในการสร้าง dto จากนั้นรวบรวม .. ฉันควรระบุ - ฉันชอบที่คุณใช้ชื่อสามัญ
Ken

0

ในการส่งออกข้อมูลไปยัง Excel คุณสามารถใช้ไลบรารี ClosedXML รายงาน ( https://github.com/ClosedXML/ClosedXML รายงาน ) เชื่อฉันสินี่เป็นห้องสมุดที่ยอดเยี่ยมและง่ายสำหรับเธอ ไลบรารีไม่จำเป็นต้องมี Excel Interop ClosedXML Report จะสร้างไฟล์ Excel โดยใช้เทมเพลตที่คุณสามารถสร้างใน Excel โดยใช้การจัดรูปแบบใด ๆ ตัวอย่างเช่น:

    var template = new XLTemplate(@".\Templates\report.xlsx");

    using (var db = new DbDemos())
    {
        var cust = db.customers.LoadWith(c => c.Orders).First();
        template.AddVariable(cust);
        template.Generate();
    }

    template.SaveAs(outputFile);

0
Private tmr As System.Windows.Forms.Timer

Private Sub TestExcel() Handles Button1.Click

    '// Initial data: SQL Server table with 6 columns and 293000 rows.


    '// Data table holding all data
    Dim dt As New DataTable("F161")

    '// Create connection
    Dim conn As New SqlConnection("Server=MYSERVER;Database=Test;Trusted_Connection=Yes;")
    Dim fAdapter As New SqlDataAdapter With
    {
        .SelectCommand = New SqlCommand($"SELECT * FROM dbo.MyTable", conn)
    }

    '// Fill DataTable
    fAdapter.Fill(dt)

    '// Create Excel application
    Dim xlApp As New Excel.Application With {.Visible = True}

    '// Temporarily disable screen updating
    xlApp.ScreenUpdating = False

    '// Create brand new workbook
    Dim xlBook As Excel.Workbook = xlApp.Workbooks.Add()
    Dim xlSheet As Excel.Worksheet = DirectCast(xlBook.Sheets(1), Excel.Worksheet)

    '// Get number of rows
    Dim rows_count = dt.Rows.Count
    '// Get number of columns
    Dim cols_count = dt.Columns.Count

    '// Here 's the core idea: after receiving data
    '// you need to create an array and transfer it to sheet.
    '// Why array?
    '// Because it's the fastest way to transfer data to Excel's sheet.
    '// So, we have two tasks:
    '// 1) Create array
    '// 2) Transfer array to sheet

    '// =========================================================
    '// TASK 1: Create array
    '// =========================================================
    '// In order to create array, we need to know that
    '// Excel's Range object expects 2-D array whose lower bounds
    '// of both dimensions start from 1.
    '// This means you can't use C# array.
    '// You need to manually create such array.
    '// Since we already calculated number of rows and columns,
    '// we can use these numbers in creating array.
    Dim arr = Array.CreateInstance(GetType(Object), {rows_count, cols_count}, {1, 1})

    '// Fill array
    For r = 0 To rows_count - 1
        For c = 0 To cols_count - 1
            arr(r + 1, c + 1) = dt.Rows(r)(c)
        Next
    Next

    '// =========================================================
    '// TASK 2: Transfer array to sheet
    '// =========================================================
    '// Now we need to transfer array to sheet.
    '// So, how transfer array to sheet fast?
    '// 
    '// THE FASTEST WAY TO TRANSFER DATA TO SHEET IS TO ASSIGN ARRAY TO RANGE.
    '// We could, of course, hard-code values, but Resize property
    '// makes this work a breeze:
    xlSheet.Range("A1").Resize.Resize(rows_count, cols_count).Value = arr

    '// If we decide to dump data by iterating over array,
    '// it will take LOTS of time.
    '// For r = 1 To rows_count
    '//     For c = 1 To cols_count
    '//         xlSheet.Cells(r, c) = arr(r, c)
    '//     Next
    '// Next

    '// Here are time results:
    '// 1) Assigning array to Range: 3 seconds
    '// 2) Iterating over array: 45 minutes

    '// Turn updating on
    xlApp.ScreenUpdating = True
    xlApp = Nothing
    xlBook = Nothing
    xlSheet = Nothing

    '// Here we have another problem:
    '// creating array took lots of memory (about 150 MB).
    '// Using 'GC.Collect()', by unknown reason, doesn't help here.
    '// However, if you run GC.Collect() AFTER this procedure is finished
    '// (say, by pressing another button and calling another procedure),
    '// then the memory is cleaned up.
    '// I was wondering how to avoid creating some extra button to just release memory,
    '// so I came up with the idea to use timer to call GC.
    '// After 2 seconds GC collects all generations.
    '// Do not forget to dispose timer since we need it only once.

    tmr = New Timer()
    AddHandler tmr.Tick,
        Sub()
            GC.Collect()
            GC.WaitForPendingFinalizers()
            GC.WaitForFullGCComplete()
            tmr.Dispose()
        End Sub
    tmr.Interval = TimeSpan.FromSeconds(2).TotalMilliseconds()
    tmr.Start()

End Sub

0

โค้ดตัวอย่างล้วนๆ (ในกรณีที่อาจช่วยให้ใครบางคนมีแนวคิดบางอย่าง) อ้างอิงจากคำตอบของTomasz Wiśniewskiจากที่นี่: https://stackoverflow.com/a/21079709/2717521

ปุ่มส่งออกหน้าต่างหลัก:

public int counter;

public void SaveToExcel(object sender, RoutedEventArgs e)
{
    counter = 1;
    CountChecker();
}

public void CountChecker()
{
    string filename = GlobalStrings.building_house_address;
    string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\";


    if (CurrentID != 0)
    {
        if (!File.Exists(path + filename + ".xlsx"))
        {
            DataGridParts.Export(path + filename);
            MessageBoxEx.Show(this, "Shranjeno na namizje");
        }
        else
        {
            if (!File.Exists(path + "\\" + filename + " (" + (counter) + ")" + ".xlsx"))
            {
                DataGridParts.Export(path + filename + " (" + (counter) + ")");
                MessageBoxEx.Show(this, "Shranjeno na namizje");
            }
            else
            {
                counter++;
                CountChecker();
            }
        }
    }
    else
    {
        MessageBoxEx.Show(this, "Izbran ni noben naslov!");
    }
}

คลาส ExportToExcel:

using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

namespace CBUGIS
{
    public static class ExportToExcel
    {
        /// <summary>
        /// Export DataTable to Excel file
        /// </summary>
        /// <param name="DataTable">Source DataTable</param>
        /// <param name="ExcelFilePath">Path to result file name</param>
        public static void Export(this System.Data.DataTable DataTable, string ExcelFilePath = null)
        {
            int ColumnsCount;
            int RowShift = 5;

            ColumnsCount = DataTable.Columns.Count;

            // load excel, and create a new workbook
            Application Excel = new Application();
            Excel.Workbooks.Add();

            // single worksheet
            _Worksheet Worksheet = Excel.ActiveSheet;
            Excel.Sheets[1].Name = "CBUGIS";

            Worksheet.Columns.NumberFormat = "@";
            Worksheet.Columns.HorizontalAlignment = XlHAlign.xlHAlignLeft;

            object[,] Title = new object[3, 1]; // Array Size

            if (GlobalStrings.building_alterantive_addresses.Length == 0)
            {
                if (GlobalStrings.building_postcode.Length != 0)
                {
                    Title[0, 0] = "NASLOV: " + GlobalStrings.building_house_street + " " + GlobalStrings.building_house_number + GlobalStrings.building_house_id + ", " + GlobalStrings.building_postcode + " " + GlobalStrings.building_area;
                    Title[1, 0] = "K.O.: " + GlobalStrings.building_cadastral_community + ", ŠT.STAVBE: " + GlobalStrings.building_building_number + ", ŠT.PARCELE: " + GlobalStrings.building_plot_number;
                }
                else
                {
                    Title[0, 0] = "NASLOV: " + GlobalStrings.building_house_street + " " + GlobalStrings.building_house_number + GlobalStrings.building_house_id;
                    Title[1, 0] = "K.O.: " + GlobalStrings.building_cadastral_community + ", ŠT.STAVBE: " + GlobalStrings.building_building_number + ", " + GlobalStrings.building_plot_number;
                }
            }
            else
            {
                if (GlobalStrings.building_postcode.Length != 0)
                {
                    Title[0, 0] = "NASLOV: " + GlobalStrings.building_house_street + " " + GlobalStrings.building_house_number + GlobalStrings.building_house_id + ", " + GlobalStrings.building_postcode + " " + GlobalStrings.building_area;
                    Title[1, 0] = "K.O.: " + GlobalStrings.building_cadastral_community + ", ŠT.STAVBE: " + GlobalStrings.building_building_number + ", ŠT.PARCELE: " + GlobalStrings.building_plot_number;
                    Title[2, 0] = "GLEJ TUDI: " + GlobalStrings.building_alterantive_addresses;
                }
                else
                {
                    Title[0, 0] = "NASLOV: " + GlobalStrings.building_house_street + " " + GlobalStrings.building_house_number + GlobalStrings.building_house_id;
                    Title[1, 0] = "K.O.: " + GlobalStrings.building_cadastral_community + ", ŠT.STAVBE: " + GlobalStrings.building_building_number + ", ŠT.PARCELE: " + GlobalStrings.building_plot_number;
                    Title[2, 0] = "GLEJ TUDI: " + GlobalStrings.building_alterantive_addresses;
                }
            }

            Range TitleRange = Worksheet.get_Range((Range)(Worksheet.Cells[3, 1]), (Range)(Worksheet.Cells[1, 1]));
            TitleRange.Value = Title;
            TitleRange.Font.Bold = true;
            TitleRange.Font.Size = 10;


            object[] Header = new object[11]; // Number of Columns

            Header[0] = "DEL";
            Header[1] = "DELEŽ";
            Header[2] = "CRP";
            Header[3] = "LASTNIK";
            Header[4] = "NASLOV";
            Header[5] = "P.Š";
            Header[6] = "OBMOČJE";
            Header[7] = "DRŽAVA";
            Header[8] = "EMŠO/MAT. ŠT.";
            Header[9] = "OPIS";
            Header[10] = "OPOMBA";

            Range HeaderRange = Worksheet.get_Range((Range)(Worksheet.Cells[RowShift, 2]), (Range)(Worksheet.Cells[RowShift, 12]));
            HeaderRange.Value = Header;
            HeaderRange.Font.Bold = true;
            HeaderRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);

            // DataCells
            int RowsCount = DataTable.Rows.Count;

            object[,] Cells = new object[RowsCount, ColumnsCount];

            for (int j = 0; j < RowsCount; j++)
                for (int i = 0; i < ColumnsCount - 1; i++)
                    if (i > 1)
                    {
                        Cells[j, i - 2] = DataTable.Rows[j][i];
                    }

            Range CellRange = Worksheet.get_Range((Range)(Worksheet.Cells[RowShift +1, 2]), (Range)(Worksheet.Cells[RowShift + RowsCount, 12]));
            CellRange.Value = Cells;
            CellRange.Borders.LineStyle = XlLineStyle.xlContinuous;

            Worksheet.Columns.NumberFormat = "@";
            Worksheet.Columns[1].ColumnWidth = 0.1;
            for (int b = 1; b < 12; b++)
            {
                if (b > 1)
                {
                    Worksheet.Columns[b].AutoFit();
                }
            }

            Worksheet.PageSetup.Orientation = XlPageOrientation.xlLandscape;
            Worksheet.PageSetup.TopMargin = 0.5;
            Worksheet.PageSetup.BottomMargin = 0.5;
            Worksheet.PageSetup.RightMargin = 0.5;
            Worksheet.PageSetup.LeftMargin = 0.5;

            // check fielpath
            if (ExcelFilePath != null && ExcelFilePath != "")
            {
                Worksheet.SaveAs(ExcelFilePath);
                Excel.Quit();
                Marshal.FinalReleaseComObject(Worksheet);
                Marshal.FinalReleaseComObject(TitleRange);
                Marshal.FinalReleaseComObject(HeaderRange);
                Marshal.FinalReleaseComObject(CellRange);
                Marshal.FinalReleaseComObject(Excel);
            }
            else

            // no filepath is given
            {
                Excel.Visible = true;
            }      
        }
    }
}

0

ด้วยแพคเกจ EPPlus NuGet, มันเป็นเรื่องง่ายมาก

public class TestObject
{
    public int Col1 { get; set; }
    public int Col2 { get; set; }
    public string Col3 { get; set; }
    public DateTime Col4 { get; set; }
}

[TestMethod]
public void LoadFromCollection_MemberList_Test()
{
    ///programming/32587834/epplus-loadfromcollection-text-converted-to-number/32590626#32590626

    var TestObjectList = new List<TestObject>();
    for (var i = 0; i < 10; i++)
        TestObjectList.Add(new TestObject {Col1 = i, Col2 = i*10, Col3 = (i*10) + "E4"});

    //Create a test file
    var fi = new FileInfo(@"c:\temp\LoadFromCollection_MemberList_Test.xlsx");
    if (fi.Exists)
        fi.Delete();

    using (var pck = new ExcelPackage(fi))
    {
        //Do NOT include Col1
        var mi = typeof (TestObject)
            .GetProperties()
            .Where(pi => pi.Name != "Col1")
            .Select(pi => (MemberInfo)pi)
            .ToArray();

        var worksheet = pck.Workbook.Worksheets.Add("Sheet1");
        worksheet.Cells.LoadFromCollection(
            TestObjectList
            , true
            , TableStyles.Dark1
            , BindingFlags.Public| BindingFlags.Instance
            , mi);

        pck.Save();
    }
}

สังเกตว่าCol1ไม่อยู่ในเอาต์พุต:

ใส่คำอธิบายภาพที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.