10
votes

Problème de performance de Microsoft Office Interop

J'essaie d'exporter DataGridView Data vers Excel Fichier dans mon application Windows C # 4.0.

Nous avons utilisé Microsoft.Office.interop.excel DLL version 12.0.0.0. Cela fonctionne bien et tout p>

va bien. Mais je suis quand j'essaie d'exporter plus de 1000 datagridView enregistrements, il est trop long p>

temps.Comment puis-je améliorer la performance. P>

Veuillez consulter ce code d'aide excel ci-dessous. P >

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Office.Interop.Excel;
using Microsoft.Office.Core;
using System.Runtime.InteropServices; // For COMException
using System.Reflection; // For Missing.Value and BindingFlags
using System.Diagnostics; // to ensure EXCEL process is really killed

namespace Export.Excel
{

    #region InstanceFields

    //Instance Fields
    //public delegate void ProgressHandler(object sender, ProgressEventArgs e);
    //public event ProgressHandler prg;
    private System.Data.DataView dv;
    private Style styleRows;
    private Style styleColumnHeadings;
    private Microsoft.Office.Interop.Excel.Application EXL;
    private Workbook workbook;
    private Sheets sheets;
    private Worksheet worksheet;
    private string[,] myTemplateValues;
    private int position;
    private System.Globalization.CultureInfo cl;
    private Type _ResourceType;

    #endregion

    #region Constructor

    //Constructs a new export2Excel object. The user must
    //call the createExcelDocument method once a valid export2Excel
    //object has been instantiated

    public ExportExcelFormat(string culture, Type type)
    {
        cl = new System.Globalization.CultureInfo(culture);
        _ResourceType = type;
    }

    #endregion


    #region EXCEL : ExportToExcel
    //Exports a DataView to Excel. The following steps are carried out
    //in order to export the DataView to Excel
    //Create Excel Objects
    //Create Column & Row Workbook Cell Rendering Styles
    //Fill Worksheet With DataView
    //Add Auto Shapes To Excel Worksheet
    //Select All Used Cells
    //Create Headers/Footers
    //Set Status Finished
    //Save workbook & Tidy up all objects
    //@param dv : DataView to use
    //@param path : The path to save/open the EXCEL file to/from
    //@param sheetName : The target sheet within the EXCEL file
    public void ExportToExcel(System.Data.DataView dv, string path, string sheetName, string[] UnWantedColumns)
    {
        try
        {
            //Assign Instance Fields
            this.dv = dv;

            #region NEW EXCEL DOCUMENT : Create Excel Objects

            //create new EXCEL application
            EXL = new Microsoft.Office.Interop.Excel.ApplicationClass();
            //index to hold location of the requested sheetName in the workbook sheets
            //collection
            int indexOfsheetName;

            #region FILE EXISTS
            //Does the file exist for the given path
            if (File.Exists(path))
            {

                //Yes file exists, so open the file
                workbook = EXL.Workbooks.Open(path,
                    0, false, 5, "", "", false, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "",
                    true, false, 0, true, false, false);

                //get the workbook sheets collection
                sheets = workbook.Sheets;

                //set the location of the requested sheetName to -1, need to find where
                //it is. It may not actually exist
                indexOfsheetName = -1;

                //loop through the sheets collection
                for (int i = 1; i <= sheets.Count; i++)
                {
                    //get the current worksheet at index (i)
                    worksheet = (Worksheet)sheets.get_Item(i);

                    //is the current worksheet the sheetName that was requested
                    if (worksheet.Name.ToString().Equals(sheetName))
                    {
                        //yes it is, so store its index
                        indexOfsheetName = i;

                        //Select all cells, and clear the contents
                        Microsoft.Office.Interop.Excel.Range myAllRange = worksheet.Cells;
                        myAllRange.Select();
                        myAllRange.CurrentRegion.Select();
                        myAllRange.ClearContents();
                    }
                }

                //At this point it is known that the sheetName that was requested
                //does not exist within the found file, so create a new sheet within the
                //sheets collection
                if (indexOfsheetName == -1)
                {
                    //Create a new sheet for the requested sheet
                    Worksheet sh = (Worksheet)workbook.Sheets.Add(
                        Type.Missing, (Worksheet)sheets.get_Item(sheets.Count),
                        Type.Missing, Type.Missing);
                    //Change its name to that requested
                    sh.Name = sheetName;
                }
            }
            #endregion

            #region FILE DOESNT EXIST
            //No the file DOES NOT exist, so create a new file
            else
            {
                //Add a new workbook to the file
                workbook = EXL.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
                //get the workbook sheets collection
                sheets = workbook.Sheets;
                //get the new sheet
                worksheet = (Worksheet)sheets.get_Item(1);
                //Change its name to that requested
                worksheet.Name = sheetName;
            }
            #endregion

            #region get correct worksheet index for requested sheetName

            //get the workbook sheets collection
            sheets = workbook.Sheets;

            //set the location of the requested sheetName to -1, need to find where
            //it is. It will definately exist now as it has just been added
            indexOfsheetName = -1;

            //loop through the sheets collection
            for (int i = 1; i <= sheets.Count; i++)
            {
                //get the current worksheet at index (i)
                worksheet = (Worksheet)sheets.get_Item(i);



                //is the current worksheet the sheetName that was requested
                if (worksheet.Name.ToString().Equals(sheetName))
                {
                    //yes it is, so store its index
                    indexOfsheetName = i;
                }
            }

            //set the worksheet that the DataView should write to, to the known index of the
            //requested sheet
            worksheet = (Worksheet)sheets.get_Item(indexOfsheetName);
            #endregion

            #endregion

            // Set styles 1st
            SetUpStyles();
            //Fill EXCEL worksheet with DataView values
            fillWorksheet_WithDataView(UnWantedColumns);
            //Add the autoshapes to EXCEL
            //AddAutoShapesToExcel();
            //Select all used cells within current worksheet
            SelectAllUsedCells();

            try
            {
                workbook.Close(true, path, Type.Missing);
                EXL.UserControl = false;
                EXL.Quit();
                EXL = null;
                //kill the EXCEL process as a safety measure
                killExcel();
            }
            catch (COMException cex)
            {

            }
            catch (Exception ex)
            {

            }
        }
        catch (Exception ex)
        {

        }
    }
    #endregion

    #region EXCEL : UseTemplate
    //Exports a DataView to Excel. The following steps are carried out
    //in order to export the DataView to Excel
    //Create Excel Objects And Open Template File
    //Select All Used Cells
    //Create Headers/Footers
    //Set Status Finished
    //Save workbook & Tidy up all objects
    //@param path : The path to save/open the EXCEL file to/from
    public void UseTemplate(string path, string templatePath, string[,] myTemplateValues)
    {
        try
        {
            this.myTemplateValues = myTemplateValues;
            //create new EXCEL application
            EXL = new Microsoft.Office.Interop.Excel.ApplicationClass();
            //Yes file exists, so open the file
            workbook = EXL.Workbooks.Open(templatePath,
                0, false, 5, "", "", false, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "",
                true, false, 0, true, false, false);
            //get the workbook sheets collection
            sheets = workbook.Sheets;
            //get the new sheet
            worksheet = (Worksheet)sheets.get_Item(1);
            //Change its name to that requested
            worksheet.Name = "ATemplate";
            //Fills the Excel Template File Selected With A 2D Test Array
            fillTemplate_WithTestValues();
            //Select all used cells within current worksheet
            SelectAllUsedCells();

            try
            {
                workbook.Close(true, path, Type.Missing);
                EXL.UserControl = false;
                EXL.Quit();
                EXL = null;
                //kill the EXCEL process as a safety measure
                killExcel();
            }
            catch (COMException)
            {
            }
        }
        catch (Exception ex)
        {
        }
    }
    #endregion

    #region STEP 1 : Create Column & Row Workbook Cell Rendering Styles
    //Creates 2 Custom styles for the workbook These styles are
    //  styleColumnHeadings
    //  styleRows
    //These 2 styles are used when filling the individual Excel cells with the
    //DataView values. If the current cell relates to a DataView column heading
    //then the style styleColumnHeadings will be used to render the current cell.
    //If the current cell relates to a DataView row then the style styleRows will
    //be used to render the current cell.
    private void SetUpStyles()
    {
        // Style styleColumnHeadings
        try
        {
            styleColumnHeadings = workbook.Styles["styleColumnHeadings"];
        }
        // Style doesn't exist yet.
        catch
        {
            styleColumnHeadings = workbook.Styles.Add("styleColumnHeadings", Type.Missing);
            styleColumnHeadings.Font.Name = "Arial";
            styleColumnHeadings.Font.Size = 12;
            styleColumnHeadings.Font.Bold = true;
        }

        // Style styleRows
        try
        {

            styleRows = workbook.Styles["styleRows"];
        }
        // Style doesn't exist yet.
        catch
        {
            styleRows = workbook.Styles.Add("styleRows", Type.Missing);
            styleRows.Font.Name = "Verdana";
            styleRows.Font.Size = 9;
        }
    }
    #endregion

    #region STEP 2 : Fill Worksheet With DataView
    //Fills an Excel worksheet with the values contained in the DataView
    //parameter
    private void fillWorksheet_WithDataView(string[] UnWantedColumns)
    {
        position = 0;
        //Add DataView Columns To Worksheet
        int row = 1;
        int col = 1;
        // Remove unwanted columns in the loop
        int total = dv.Table.Columns.Count - UnWantedColumns.Count();
        // Loop thought the columns
        for (int i = 0; i < total; i++)
        {

            fillExcelCell(worksheet, row, col++, dv.Table.Columns[i].ToString(), styleColumnHeadings.Name, UnWantedColumns);
        }

        //Add DataView Rows To Worksheet
        row = 2;
        col = 1;

        for (int i = 0; i < dv.Table.Rows.Count; i++)
        {

            for (int j = 0; j < dv.Table.Columns.Count; j++)
            {
                fillExcelCell(worksheet, row, col++, dv[i][j].ToString(), styleRows.Name, UnWantedColumns);
            }
            col = 1;
            row++;

            position = (100 / dv.Table.Rows.Count) * row + 2;
        }
    }
    #endregion

    #region STEP 3 : Fill Individual Cell and Render Using Predefined Style
    //Formats the current cell based on the Style setting parameter name
    //provided here
    //@param worksheet : The worksheet
    //@param row : Current row
    //@param col : Current Column
    //@param Value : The value for the cell
    //@param StyleName : The style name to use
    private void fillExcelCell(Worksheet worksheet, int row, int col, Object Value, string StyleName, string[] UnWantedColumns)
    {
        if (!UnWantedColumns.Contains(Value.ToString()))
        {
            Range rng = (Range)worksheet.Cells[row, col];
            rng.NumberFormat = "@";
            rng.Select();
            rng.Value2 = Value.ToString();
            rng.Style = StyleName;
            rng.Columns.EntireColumn.AutoFit();
        }
    }
    #endregion

    #region STEP 4 : Add Auto Shapes To Excel Worksheet
    //Add some WordArt objecs to the Excel worksheet
    private void AddAutoShapesToExcel()
    {
        //Method fields
        float txtSize = 80;
        float Left = 100.0F;
        float Top = 100.0F;
        //Have 2 objects
        int[] numShapes = new int[2];
        Microsoft.Office.Interop.Excel.Shape[] myShapes = new Microsoft.Office.Interop.Excel.Shape[numShapes.Length];

        try
        {
            //loop through the object count
            for (int i = 0; i < numShapes.Length; i++)
            {

                //Add the object to Excel
                myShapes[i] = worksheet.Shapes.AddTextEffect(MsoPresetTextEffect.msoTextEffect1, "DRAFT", "Arial Black",
                    txtSize, MsoTriState.msoFalse, MsoTriState.msoFalse, (Left * (i * 3)), Top);

                //Manipulate the object settings
                myShapes[i].Rotation = 45F;
                myShapes[i].Fill.Visible = Microsoft.Office.Core.MsoTriState.msoFalse;
                myShapes[i].Fill.Transparency = 0F;
                myShapes[i].Line.Weight = 1.75F;
                myShapes[i].Line.DashStyle = MsoLineDashStyle.msoLineSolid;
                myShapes[i].Line.Transparency = 0F;
                myShapes[i].Line.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
                myShapes[i].Line.ForeColor.RGB = (0 << 16) | (0 << 8) | 0;
                myShapes[i].Line.BackColor.RGB = (255 << 16) | (255 << 8) | 255;
            }
        }
        catch (Exception ex)
        {
        }

    }
    #endregion

    #region STEP 5 : Select All Used Cells
    //Selects all used cells for the Excel worksheet
    private void SelectAllUsedCells()
    {
        Microsoft.Office.Interop.Excel.Range myAllRange = worksheet.Cells;
        myAllRange.Select();
        myAllRange.CurrentRegion.Select();
    }
    #endregion

    #region STEP 6 : Fill Template With Test Values
    //Fills the Excel Template File Selected With A 2D Test Array parameter
    private void fillTemplate_WithTestValues()
    {
        //Initilaise the correct Start Row/Column to match the Template
        int StartRow = 3;
        int StartCol = 2;

        position = 0;

        // Display the array elements within the Output window, make sure its correct before
        for (int i = 0; i <= myTemplateValues.GetUpperBound(0); i++)
        {
            //loop through array and put into EXCEL template
            for (int j = 0; j <= myTemplateValues.GetUpperBound(1); j++)
            {
                //update position in progress bar
                position = (100 / myTemplateValues.Length) * i;

                //put into EXCEL template
                Range rng = (Range)worksheet.Cells[StartRow, StartCol++];
                rng.Select();
                rng.Value2 = myTemplateValues[i, j].ToString();
                rng.Rows.EntireRow.AutoFit();
            }
            //New row, so column needs to be reset
            StartCol = 2;
            StartRow++;
        }
    }

    #endregion

    #region Kill EXCEL
    //As a safety check go through all processes and make
    //doubly sure excel is shutdown. Working with COM
    //have sometimes noticed that the EXL.Quit() call
    //does always do the job
    private void killExcel()
    {
        try
        {
            Process[] ps = Process.GetProcesses();
            foreach (Process p in ps)
            {
                if (p.ProcessName.ToLower().Equals("excel"))
                {
                    p.Kill();
                }
            }
        }
        catch (Exception ex)
        {
        }
    }
    #endregion
}


4 commentaires

Après beaucoup de problèmes, j'ai déménagé dans des formats XML:

3 Réponses :


15
votes

J'ai quelques suggestions pour améliorer la performance. Seuls ils peuvent ne pas avoir beaucoup d'impact, mais ensemble, ils devraient améliorer la performance globale.

  • Masquer Excel (si ce n'est pas déjà) exl.visible = false; code>. Éteindre calcul code> ( application.calculation = xlcalculantmanual code>, s'il est n'est pas nécessaire) et scénupharnage code> aussi. LI>
  • Utilisez excel.workbooks.worksheets code> plutôt que les feuilles code> code> Collection. Li>
  • plutôt que de boucler dans toutes les feuilles de calcul, essayez de faire référence celle que vous souhaitez, en utilisant la manipulation erronée pour déterminer si la feuille existe: P> XXX PRE> LI> ul>

    Évitez Sélectionnez CODE>, il est rarement nécessaire - et lentement. Remplacer, P>

    worksheet.UsedRange.Select();    // but shouldn't be necessary
    


0 commentaires

7
votes

Vous traitez une cellule à la fois. La plage L'objet Excel peut représenter une grille bidimensionnelle.

  1. Vous pouvez construire un tableau dans .NET et définir la valeur dans un coup.
  2. ma préférence est de le faire par des colonnes. Afin que les données et la mise en forme Peut être contenu dans une unité logique.

0 commentaires

2
votes

Je m'excuse de ne pas répondre à votre question exacte, mais j'aimerais transmettre un conseil ici.

Utiliser Interop De cette manière est très lent de la nature: la communication entre les applications n'est pas la chose la plus rapide dans Windows, et Excel fait beaucoup de choses que vous n'avez pas besoin à chaque opération, même si Andy G vous a fourni certains Conseils pour limiter ses frais généraux.

Une solution solide est pas pour utiliser Excel pour exporter des données: Utilisez une bibliothèque .NET telle que aspose.Cells ou tout autre. Aspose.Cells est un peu cher mais c'est très bien. Notez que je n'ai aucun intérêt pour Aspose, je viens de l'utiliser dans plusieurs de mes projets. J'ai également utilisé SyncFusion également, moins cher et bon mais une API moins intuitive à mon avis. Il existe d'autres, y compris la bibliothèque libre MS OpenXML, qui est gratuite mais très niveau bas (je ne le conseillerais pas).

Avec de telles bibliothèques, il est très facile d'exporter des données vers un fichier Excel et la performance est excellente.


0 commentaires