2008-12-17 11 views

Odpowiedz

35

I utworzeniu klasy, która eksportuje DataGridView lub DataTable do pliku Excel. Prawdopodobnie możesz go nieco zmienić, aby zamiast tego używał Twojego DataSet (iterując w nim DataTables). Wykonuje również podstawowe formatowanie, które można również rozszerzyć.

Aby z niego skorzystać, po prostu wywołaj ExcelExport i określ nazwę pliku oraz informację, czy otworzyć plik automatycznie, czy nie po wyeksportowaniu. Też mogłem uczynić je metodami rozszerzenia, ale nie zrobiłem tego. Nie krępuj się.

Należy pamiętać, że pliki Excel można zapisać jako gloryfikowany dokument XML i to się z tego korzysta.

EDYCJA: Używa się wtedy wanilii StreamWriter, ale jak wskazano, w wielu przypadkach rzeczy nie będą poprawnie wymazywane. Teraz używa ona XmlWriter, która zrobi dla ciebie ucieczkę.

Klasa ExcelWriter obejmuje XmlWriter. Nie przejmowałem się, ale możesz chcieć zrobić trochę więcej sprawdzania błędów, aby upewnić się, że nie możesz zapisać danych komórki przed rozpoczęciem wiersza i tym podobnych. Kod znajduje się poniżej.

public class ExcelWriter : IDisposable 
{ 
    private XmlWriter _writer; 

    public enum CellStyle { General, Number, Currency, DateTime, ShortDate }; 

    public void WriteStartDocument() 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteProcessingInstruction("mso-application", "progid=\"Excel.Sheet\""); 
     _writer.WriteStartElement("ss", "Workbook", "urn:schemas-microsoft-com:office:spreadsheet"); 
     WriteExcelStyles(); 
    } 

    public void WriteEndDocument() 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteEndElement(); 
    } 

    private void WriteExcelStyleElement(CellStyle style) 
    { 
     _writer.WriteStartElement("Style", "urn:schemas-microsoft-com:office:spreadsheet"); 
     _writer.WriteAttributeString("ID", "urn:schemas-microsoft-com:office:spreadsheet", style.ToString()); 
     _writer.WriteEndElement(); 
    } 

    private void WriteExcelStyleElement(CellStyle style, string NumberFormat) 
    { 
     _writer.WriteStartElement("Style", "urn:schemas-microsoft-com:office:spreadsheet"); 

     _writer.WriteAttributeString("ID", "urn:schemas-microsoft-com:office:spreadsheet", style.ToString()); 
     _writer.WriteStartElement("NumberFormat", "urn:schemas-microsoft-com:office:spreadsheet"); 
     _writer.WriteAttributeString("Format", "urn:schemas-microsoft-com:office:spreadsheet", NumberFormat); 
     _writer.WriteEndElement(); 

     _writer.WriteEndElement(); 

    } 

    private void WriteExcelStyles() 
    { 
     _writer.WriteStartElement("Styles", "urn:schemas-microsoft-com:office:spreadsheet"); 

     WriteExcelStyleElement(CellStyle.General); 
     WriteExcelStyleElement(CellStyle.Number, "General Number"); 
     WriteExcelStyleElement(CellStyle.DateTime, "General Date"); 
     WriteExcelStyleElement(CellStyle.Currency, "Currency"); 
     WriteExcelStyleElement(CellStyle.ShortDate, "Short Date"); 

     _writer.WriteEndElement(); 
    } 

    public void WriteStartWorksheet(string name) 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteStartElement("Worksheet", "urn:schemas-microsoft-com:office:spreadsheet"); 
     _writer.WriteAttributeString("Name", "urn:schemas-microsoft-com:office:spreadsheet", name); 
     _writer.WriteStartElement("Table", "urn:schemas-microsoft-com:office:spreadsheet"); 
    } 

    public void WriteEndWorksheet() 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteEndElement(); 
     _writer.WriteEndElement(); 
    } 

    public ExcelWriter(string outputFileName) 
    { 
     XmlWriterSettings settings = new XmlWriterSettings(); 
     settings.Indent = true; 
     _writer = XmlWriter.Create(outputFileName, settings); 
    } 

    public void Close() 
    { 
     if (_writer == null) throw new InvalidOperationException("Already closed."); 

     _writer.Close(); 
     _writer = null; 
    } 

    public void WriteExcelColumnDefinition(int columnWidth) 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteStartElement("Column", "urn:schemas-microsoft-com:office:spreadsheet"); 
     _writer.WriteStartAttribute("Width", "urn:schemas-microsoft-com:office:spreadsheet"); 
     _writer.WriteValue(columnWidth); 
     _writer.WriteEndAttribute(); 
     _writer.WriteEndElement(); 
    } 

    public void WriteExcelUnstyledCell(string value) 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteStartElement("Cell", "urn:schemas-microsoft-com:office:spreadsheet"); 
     _writer.WriteStartElement("Data", "urn:schemas-microsoft-com:office:spreadsheet"); 
     _writer.WriteAttributeString("Type", "urn:schemas-microsoft-com:office:spreadsheet", "String"); 
     _writer.WriteValue(value); 
     _writer.WriteEndElement(); 
     _writer.WriteEndElement(); 
    } 

    public void WriteStartRow() 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteStartElement("Row", "urn:schemas-microsoft-com:office:spreadsheet"); 
    } 

    public void WriteEndRow() 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteEndElement(); 
    } 

    public void WriteExcelStyledCell(object value, CellStyle style) 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     _writer.WriteStartElement("Cell", "urn:schemas-microsoft-com:office:spreadsheet"); 
     _writer.WriteAttributeString("StyleID", "urn:schemas-microsoft-com:office:spreadsheet", style.ToString()); 
     _writer.WriteStartElement("Data", "urn:schemas-microsoft-com:office:spreadsheet"); 
     switch (style) 
     { 
      case CellStyle.General: 
       _writer.WriteAttributeString("Type", "urn:schemas-microsoft-com:office:spreadsheet", "String"); 
       break; 
      case CellStyle.Number: 
      case CellStyle.Currency: 
       _writer.WriteAttributeString("Type", "urn:schemas-microsoft-com:office:spreadsheet", "Number"); 
       break; 
      case CellStyle.ShortDate: 
      case CellStyle.DateTime: 
       _writer.WriteAttributeString("Type", "urn:schemas-microsoft-com:office:spreadsheet", "DateTime"); 
       break; 
     } 
     _writer.WriteValue(value); 
     // tag += String.Format("{1}\"><ss:Data ss:Type=\"DateTime\">{0:yyyy\\-MM\\-dd\\THH\\:mm\\:ss\\.fff}</ss:Data>", value, 

     _writer.WriteEndElement(); 
     _writer.WriteEndElement(); 
    } 

    public void WriteExcelAutoStyledCell(object value) 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 

     //write the <ss:Cell> and <ss:Data> tags for something 
     if (value is Int16 || value is Int32 || value is Int64 || value is SByte || 
      value is UInt16 || value is UInt32 || value is UInt64 || value is Byte) 
     { 
      WriteExcelStyledCell(value, CellStyle.Number); 
     } 
     else if (value is Single || value is Double || value is Decimal) //we'll assume it's a currency 
     { 
      WriteExcelStyledCell(value, CellStyle.Currency); 
     } 
     else if (value is DateTime) 
     { 
      //check if there's no time information and use the appropriate style 
      WriteExcelStyledCell(value, ((DateTime)value).TimeOfDay.CompareTo(new TimeSpan(0, 0, 0, 0, 0)) == 0 ? CellStyle.ShortDate : CellStyle.DateTime); 
     } 
     else 
     { 
      WriteExcelStyledCell(value, CellStyle.General); 
     } 
    } 

    #region IDisposable Members 

    public void Dispose() 
    { 
     if (_writer == null) 
      return; 

     _writer.Close(); 
     _writer = null; 
    } 

    #endregion 
} 

Następnie można wyeksportować DataTable przy użyciu następujących:

public static void ExcelExport(DataTable data, String fileName, bool openAfter) 
{ 
    //export a DataTable to Excel 
    DialogResult retry = DialogResult.Retry; 

    while (retry == DialogResult.Retry) 
    { 
     try 
     { 
      using (ExcelWriter writer = new ExcelWriter(fileName)) 
      { 
       writer.WriteStartDocument(); 

       // Write the worksheet contents 
       writer.WriteStartWorksheet("Sheet1"); 

       //Write header row 
       writer.WriteStartRow(); 
       foreach (DataColumn col in data.Columns) 
        writer.WriteExcelUnstyledCell(col.Caption); 
       writer.WriteEndRow(); 

       //write data 
       foreach (DataRow row in data.Rows) 
       { 
        writer.WriteStartRow(); 
        foreach (object o in row.ItemArray) 
        { 
         writer.WriteExcelAutoStyledCell(o); 
        } 
        writer.WriteEndRow(); 
       } 

       // Close up the document 
       writer.WriteEndWorksheet(); 
       writer.WriteEndDocument(); 
       writer.Close(); 
       if (openAfter) 
        OpenFile(fileName); 
       retry = DialogResult.Cancel; 
      } 
     } 
     catch (Exception myException) 
     { 
      retry = MessageBox.Show(myException.Message, "Excel Export", MessageBoxButtons.RetryCancel, MessageBoxIcon.Asterisk); 
     } 
    } 
} 
+1

-1: Nie próbować wygenerować XML za pomocą TextWriter w ten sposób. Po prostu wskażę jeden oczywisty problem: jeśli twoja DataTable zawiera wartość ciągu z nawiasami ostrymi, powyższy kod nie ucieknie im właściwie. – Joe

+1

Dobra uwaga. Po prostu pokazuje, że powinienem przestać myśleć o trochę więcej. Przerobiłem go, aby użyć XmlWriter. Teoretycznie powinieneś zrobić trochę więcej sprawdzania błędów przed wypuszczeniem go w bibliotece publicznej, ale jeśli jesteś pewien, że zadzwonisz do rzeczy we właściwej kolejności, jesteś "bezpieczny". –

+0

W drugiej części kodu jest błąd. Zamiast "foreach (obiekt o w dataTable.Rows)" powinno być "foreach (object o in row.ItemArray)". – Aleris

1

Tworzenie plików Excela w aplikacji .NET jest dość powszechne i przed podobne pytania były zadawane kilkakrotnie. Na przykład: here i here. Ostatnie pytanie dotyczy czytania plików programu Excel, ale większość sugerowanych rozwiązań powinna działać w obie strony.

+0

Popraw mnie, jeśli się mylę, ale mave poprosił o rozwiązanie nie na serwerze WWW? –

+0

Ah. Źle zrozumiałem twoje pytanie. Myślałem, że pytasz o sposób tworzenia plików Excela bez PISANIA jakiegokolwiek kodu. W związku z tym zmienię moją odpowiedź. –

3

Poniższa strona demonstruje sposób eksportowania DataSet (lub DataTable lub Lista <>) w "prawdziwej" Excel 2007 .xlsx.

Używa OPENXML bibliotek, więc ty NIE potrzebę posiadania Excel zainstalowany na serwerze.

MikesKnowledgeBase - ExportToExcel

Cały kod źródłowy jest dostępny, bezpłatnie, aswell jako aplikacja demo.

To bardzo łatwo dodać do swoich aplikacji, wystarczy zadzwonić jedną funkcję, przekazując w pliku programu Excel, a źródło danych:

DataSet ds = CreateSampleData(); 
string excelFilename = "C:\\Sample.xlsx"; 
CreateExcelFile.CreateExcelDocument(ds, excelFilename); 

Nadzieja to pomaga.

+0

Używam tego. Nie widziałem umowy licencyjnej? – Melanie

1
using XL = Microsoft.Office.Interop.Excel; 
using System.Reflection; 

public static void Datasource(DataTable dt) 
     { 

      XL.Application oXL; 

      XL._Workbook oWB; 

      XL._Worksheet oSheet; 

      XL.Range oRng; 


      try 
      { 
       oXL = new XL.Application(); 

       Application.DoEvents(); 

       oXL.Visible = false; 

       //Get a new workbook. 

       oWB = (XL._Workbook)(oXL.Workbooks.Add(Missing.Value)); 

       oSheet = (XL._Worksheet)oWB.ActiveSheet; 

       //System.Data.DataTable dtGridData=ds.Tables[0]; 


       int iRow = 2; 

       if (dt.Rows.Count > 0) 
       { 


        for (int j = 0; j < dt.Columns.Count; j++) 
        { 

         oSheet.Cells[1, j + 1] = dt.Columns[j].ColumnName; 

        } 

        // For each row, print the values of each column. 

        for (int rowNo = 0; rowNo < dt.Rows.Count; rowNo++) 
        { 

         for (int colNo = 0; colNo < dt.Columns.Count; colNo++) 
         { 

          oSheet.Cells[iRow, colNo + 1] = dt.Rows[rowNo][colNo].ToString(); 

         } 
         iRow++; 

        } 

        iRow++; 

       } 

       oRng = oSheet.get_Range("A1", "IV1"); 

       oRng.EntireColumn.AutoFit(); 

       oXL.Visible = true; 

      } 

      catch (Exception theException) 
      { 

       throw theException; 

      } 
      finally 
      { 
       oXL = null; 

       oWB = null; 

       oSheet = null; 

       oRng = null; 
      } 

     } 

Import from Excel to datatable 

DataTable dtTable = new DataTable(); 
      DataColumn col = new DataColumn("Rfid"); 
      dtTable.Columns.Add(col); 
      DataRow drRow; 

       Microsoft.Office.Interop.Excel.Application ExcelObj = 
        new Microsoft.Office.Interop.Excel.Application(); 
       Microsoft.Office.Interop.Excel.Workbook theWorkbook = 
         ExcelObj.Workbooks.Open(txt_FilePath.Text, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
               Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
               Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); 
       Microsoft.Office.Interop.Excel.Sheets sheets = theWorkbook.Worksheets; 
       try 
       { 
        for (int sht = 1; sht <= sheets.Count; sht++) 
        { 
         Microsoft.Office.Interop.Excel.Worksheet worksheet = 
           (Microsoft.Office.Interop.Excel.Worksheet)sheets.get_Item(sht); 

         for (int i = 2; i <= worksheet.UsedRange.Rows.Count; i++) 
         { 
          Microsoft.Office.Interop.Excel.Range range = worksheet.get_Range("A" + i.ToString(), "B" + i.ToString()); 
          System.Array myvalues = (System.Array)range.Cells.Value2; 
          String name = Convert.ToString(myvalues.GetValue(1, 1)); 
          if (string.IsNullOrEmpty(name) == false) 
          { 
           drRow = dtTable.NewRow(); 
           drRow["Rfid"] = name; 
           dtTable.Rows.Add(drRow); 
          } 
         } 
         Marshal.ReleaseComObject(worksheet); 
         worksheet = null; 
        } 
       return dtTable; 

      } 
      catch 
      { 
       throw; 
      } 
      finally 
      { 
       // Marshal.ReleaseComObject(worksheet); 
       Marshal.ReleaseComObject(sheets); 
       Marshal.ReleaseComObject(theWorkbook); 
       Marshal.ReleaseComObject(ExcelObj); 
       //worksheet = null; 
       sheets = null; 
       theWorkbook = null; 
       ExcelObj = null; 
      } 
+0

Spróbuj podać objaśnienia do kodu. –

1

To był post z bardzo pomocny górnym odpowiedź, ale uważam, że brakowało, bo nie było w prosty sposób zaimportować plik XML z powrotem do DataTable.I skończyło się na tym, aby napisać własną rękę, i myślałem, że będzie to zrobić tutaj na wypadek gdyby ktoś jeszcze był w tej samej łodzi (google był wyjątkowo pomocne w tym względzie):

public static DataTable ImportExcelXML(string Filename) 
    { 
     //create a new dataset to load in the XML file 
     DataSet DS = new DataSet(); 
     //Read the XML file into the dataset 
     DS.ReadXml(Filename); 
     //Create a new datatable to store the raw Data 
     DataTable Raw = new DataTable(); 
     //assign the raw data from the file to the datatable 
     Raw = DS.Tables["Data"]; 
     //count the number of columns in the XML file 
     int ColumnNumber = Raw.Columns.Count; 
     //create a datatable to store formatted Import Data 
     DataTable ImportData = new DataTable(); 
     //create a string list to store the cell data of each row 
     List<string> RowData = new List<string>(); 
     //loop through each row in the raw data table 
     for (int Counter = 0; Counter < Raw.Rows.Count; Counter++) 
     { 
      //if the data in the row is a colum header 
      if (Counter < ColumnNumber) 
      { 
       //add the column name to our formatted datatable 
       ImportData.Columns.Add(Raw.Rows[Counter].ItemArray.GetValue(1).ToString()); 
      } 
      else 
      { 
       //if the row # (1 row = 1 cell from the excel file) from the raw datatable is divisable evenly by the number of columns in the formated import datatable AND this is not the 1st row of the raw table data after the headers 
       if ((Counter % ColumnNumber == 0) && (Counter != ColumnNumber)) 
       { 
        //add the row we just built to the formatted import datatable 
        ImportData.Rows.Add(GenerateRow(ImportData, RowData)); 
        //clear rowdata list in preperation for the next row 
        RowData.Clear(); 
       } 
       //add the current cell data value from the raw datatable to the string list of cell values for the next row to be added to the formatted input datatable 
       RowData.Add(Raw.Rows[Counter].ItemArray.GetValue(1).ToString()); 
      } 
     } 
     //add the final row 
     ImportData.Rows.Add(GenerateRow(ImportData, RowData)); 

     return ImportData; 
    } 

    public static DataRow GenerateRow(DataTable ImportData, List<string> RowData) 
    { 
     //create a counter to keep track of the column position during row composition 
     int ColumnPosition = 0; 
     //make a new datarow based on the schema of the formated import datatable 
     DataRow NewRow = ImportData.NewRow(); 
     //for each string cell value collected for the RowData list for this row 
     foreach (string CellData in RowData) 
     { 
      //add the cell value to the new datarow 
      NewRow[ImportData.Columns[ColumnPosition].ColumnName] = CellData; 
      //incriment column position in the new row 
      ColumnPosition++; 
     } 
     //return the generated row 
     return NewRow; 
    } 
1

kod mieć problem z wartościami null.

public void WriteExcelAutoStyledCell(object value) 
    { 
     //solve null values 
     if (value is DBNull) return; 
1

bym dodał to do komentarzy, ale jestem nowy w stos, więc jestem w stanie skomentować. Używając rozwiązania Lc. Dodałem kolejną funkcję, która testuje znaki ciągów dla niepoprawnych znaków XML. Podczas eksportowania do programu Excel czasami pojawiały się znaki, które powodowały niepowodzenie eksportu.

Będziesz musiał zmodyfikować jedną z funkcji, która jest w kodzie LCA.

public void WriteExcelAutoStyledCell(object value) 
    { 
     if (_writer == null) throw new InvalidOperationException("Cannot write after closing."); 
     string newValue = string.Empty; 

     try 
     { 
      //write the <ss:Cell> and <ss:Data> tags for something 
      if (value is Int16 || value is Int32 || value is Int64 || value is SByte || 
       value is UInt16 || value is UInt32 || value is UInt64 || value is Byte) 
      { 
       WriteExcelStyledCell(value, CellStyle.Number); 
      } 
      else if (value is Single || value is Double || value is Decimal) //we'll assume it's a currency 
      { 
       WriteExcelStyledCell(value, CellStyle.Currency); 
      } 
      else if (value is DateTime) 
      { 
       //check if there's no time information and use the appropriate style 
       WriteExcelStyledCell(value, ((DateTime)value).TimeOfDay.CompareTo(new TimeSpan(0, 0, 0, 0, 0)) == 0 ? CellStyle.ShortDate : CellStyle.DateTime); 
      } 
      else 
      { 
       newValue = CheckXmlCompatibleValues(value.ToString()).ToString(); 
       WriteExcelStyledCell(newValue, CellStyle.General); 
      } 
     } 
     catch (Exception thisException) 
     { 
      throw new InvalidOperationException(thisException.Message.ToString()); 
     } 
    } 

I dodać tę funkcję do 'ExcelWriter' klasa

public string CheckXmlCompatibleValues(string value) 
    { 
     string newValue = string.Empty; 
     bool found = false; 

     foreach (char c in value) 
     { 
      if (XmlConvert.IsXmlChar(c)) 
       newValue += c.ToString(); 
      else 
       found = true; 
     } 

     return newValue.ToString(); 
    } 

lc. Dzięki za kod!

0

Firma Microsoft ma wbudowane rozwiązanie do importowania/eksportowania plików Excel. Nie jest to najprostsza biblioteka, ale generalnie działa lepiej niż inne wymienione powyżej.

Wymagana biblioteka jest zawarta w pakiecie Office i można ją znaleźć pod listą zestawów modułów w witrynie Microsoft.Office.Interop.Excel.

Oto przykładowy kod:

using Excel = Microsoft.Office.Interop.Excel; 

Excel.Application app = new Excel.Application(); 

//Open existing workbook 
//Excel.Workbook workbook = xlApp.Workbooks.Open(fileName); 

//Create new workbook 
Excel.Workbook workbook = app.Workbooks.Add(); 

Excel.Worksheet worksheet = workbook.ActiveSheet; 

worksheet.Cells[1,1] = "Hello world!"; // Indexes start at 1, because Excel 
workbook.SaveAs("C:\\MyWorkbook.xlsx"); 
workbook.Close(); 
app.Quit(); 
Powiązane problemy