0

So I have an application that will create an excel file with a specified name, I want my application to read and display this file in my applications DataGridView on startup, code I have so far is as follows:

    private void OpenExcelFile()
            {
                
                System.Data.DataTable dt = new System.Data.DataTable(); //container for our excel data
    
                foreach (var file in Directory.GetFiles(this.ExcelFolderPath).Where(p => p.Contains("Contacts")))
                {
                    try
                    {
                        //Create Object for Microsoft.Office.Interop.Excel that will be use to read excel file
    
                        Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
    
                        Microsoft.Office.Interop.Excel.Workbook excelWorkbook = excelApp.Workbooks.Open(file);
    
                        Microsoft.Office.Interop.Excel._Worksheet excelWorksheet = excelWorkbook.Sheets[1];
    
                        Microsoft.Office.Interop.Excel.Range excelRange = excelWorksheet.UsedRange;
    
                        int rowCount = excelRange.Rows.Count; //get row count of excel data
    
                        int colCount = excelRange.Columns.Count; // get column count of excel data

                        GC.Collect();
                        GC.WaitForPendingFinalizers();
                        Marshal.ReleaseComObject(excelRange);
                        Marshal.ReleaseComObject(excelWorksheet);
                        //quit apps
                        excelWorkbook.Close();
                        Marshal.ReleaseComObject(excelWorkbook);
                        excelApp.Quit();
                        Marshal.ReleaseComObject(excelApp);
                    }
                    catch (Exception ex)
                    {
                    MessageBox.Show(ex.Message);
                }

This will search and find my file and will also count the amount of columns and rows in the table, the code at the bottom will close the excel process, after this I am not sure what my next step is, I know I have to open the excel file but am unsure on how to do this. Any help is appreciated.

jacko10101
  • 21
  • 6
  • Well… you have the row and column count of the cells in the worksheet, you could loop through the cells and add them to some collection of your choice… what are you wanting to do with the data? – JohnG Jun 24 '22 at 12:11
  • I am just trying to display the data in my datagridview, I can loop through the cells but how then do I add this and display onto my application? – jacko10101 Jun 24 '22 at 12:16
  • 1
    There are probably a couple of ways to read the Excel file into a data structure like a `DataTable`. I have to first ask… are you stuck on using Interop? There are better third-party libraries that you could use… some are free for non-commercial use. However, I am aware that you may not have a choice. If you are stuck using Interop, then create a simple `string` `DataTable` with the number of columns and then loop through the rows/columns in the worksheet and add the rows to the `DataTable`. – JohnG Jun 24 '22 at 12:32
  • The following may provide some clues: https://stackoverflow.com/questions/65045576/write-to-excel-using-c-function-that-will-enter-one-value-into-an-excel-ever/65053425#65053425 and https://stackoverflow.com/questions/67832440/how-to-load-on-demand-excel-rows-in-a-data-table-c-sharp/68156756#68156756 . You may also consider using [ClosedXML](https://github.com/ClosedXML/ClosedXML) or [EPPlus](https://github.com/JanKallman/EPPlus). – Tu deschizi eu inchid Jun 24 '22 at 14:36

1 Answers1

1

If you are stuck using Interop, then the code below may help. One issue in your current code is the way the code closes and releases the Excel COM objects. Granted you do have this code in a try/catch block, however, if the code crashes BEFORE it gets to the close and release code… then those COM objects do NOT get closed or released and end up being a leaking resource.

I suggest you put the close and release code in the finally portion of the try/catch/finally statement. This way the COM objects “should” technically get released even if the code crashes while working with the Excel file.


It appears you have defined a DataTable called dt at the start of your code… but it is never used. So, lets use it… I have renamed it to NewDT.

First step is to create the DataTable columns. We will assume the first row in the Excel file is a header row. We will use the first row for the column names. We will also assume that all the column “types” will be of a string type. You could easily convert the strings to the proper type if needed in this code. However, you would need to know before hand what those “types” are and “which” columns they belong to. In this example they are all strings to simplify the code.

After we create the columns for the DataTable then all that is left is to loop through the Excel cells and grab the cells string value and add it to the table. This sounds straight forward and simple enough, however, there is one caveat you want to keep in mind… Looping through Excel Ranges can be expensive… and I would avoid it if possible.

To avoid this, we can use a two dimensional object array and grab the WHOLE UsedRange and read it into this array… something like…

excelRange = excelWorksheet.UsedRange;
object[,] data = (object[,])excelRange.Cells.Value;

This is the array we will loop through to get the cell values. This will speed things up if the Excel file is large. SPECIAL NOTE: Since we will be looping through this object array data you should be aware that it will have its starting index at ONE (1) and NOT zero (0). This is coming from Excel and we just need to be aware of this.

The first row in the array contains the column names. So, we want to use this row to define the column names in the NewDT DataTable. This simple loop would look something like…

for (int i = 1; i <= colCount; i++) {
  if (data[1, i] != null) {
    NewDT.Columns.Add(data[1, i].ToString(), typeof(string));
  }
  else {
    NewDT.Columns.Add("", typeof(string));
  }
}

NOTE: It may be wise to check for "duplicate" columns names here to avoid the duplicate names exception. I will leave this for you to do.

Next, we want to loop through each row in the array and collect the column/cell values and add that row to the NewDT DataTable. This may look something like…

DataRow curRow = null;
for (int i = 2; i <= rowCount; i++) {
  curRow = NewDT.NewRow();
  for (int j = 1; j <= colCount; j++) {
    if (data[i, j] != null) {
      curRow[j - 1] = data[i, j].ToString();
    }
  }
  NewDT.Rows.Add(curRow);
}

NOTE the curRow’s column/cell index is “J – 1” since the DataTable column indexes start at zero (0).

That should pretty much do it. Now you can add the DataTable as a DataSource to the grid… dataGridView1.DataSource = NewDT;

The complete updated code is below…

string ExcelFolderPath = @"PathToYourFile";

private void Form1_Load(object sender, EventArgs e) {
  OpenExcelFile();
}


private void OpenExcelFile() {
  DataTable NewDT;

  foreach (var file in Directory.GetFiles(this.ExcelFolderPath).Where(p => p.Contains("Contacts"))) {
    Microsoft.Office.Interop.Excel.Application excelApp = null;
    Microsoft.Office.Interop.Excel.Workbook excelWorkbook = null;
    Microsoft.Office.Interop.Excel._Worksheet excelWorksheet = null;
    Microsoft.Office.Interop.Excel.Range excelRange = null;
    try {
      excelApp = new Microsoft.Office.Interop.Excel.Application();
      excelWorkbook = excelApp.Workbooks.Open(file);
      excelWorksheet = excelWorkbook.Sheets[1];
      excelRange = excelWorksheet.UsedRange;
      int rowCount = excelRange.Rows.Count;
      int colCount = excelRange.Columns.Count;
      object[,] data = (object[,])excelRange.Cells.Value;
      NewDT = new DataTable();
      for (int i = 1; i <= colCount; i++) {
        if (data[1, i] != null) {
          NewDT.Columns.Add(data[1, i].ToString(), typeof(string));
        }
        else {
          NewDT.Columns.Add("", typeof(string));
        }
      }
      DataRow curRow = null;
      for (int i = 2; i <= rowCount; i++) {
        curRow = NewDT.NewRow();
        for (int j = 1; j <= colCount; j++) {
          if (data[i, j] != null) {
            curRow[j - 1] = data[i, j].ToString();
          }
        }
        NewDT.Rows.Add(curRow);
      }
      dataGridView1.DataSource = NewDT;
    }
    catch (Exception ex) {
      MessageBox.Show(ex.Message);
    }
    finally {
      //GC.Collect();
      //GC.WaitForPendingFinalizers();
      if (excelRange != null) {
        Marshal.ReleaseComObject(excelRange);
      }
      if (excelWorksheet != null) {
        Marshal.ReleaseComObject(excelWorksheet);
      }
      if (excelWorkbook != null) {
        excelWorkbook.Close();
        Marshal.ReleaseComObject(excelWorkbook);
      }
      if (excelApp != null) {
        excelApp.Quit();
        Marshal.ReleaseComObject(excelApp);
      }
    }
  }
}

I hope this makes sense and helps.

JohnG
  • 9,259
  • 2
  • 20
  • 29
  • If one adds the following [using directive](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive): `using Excel = Microsoft.Office.Interop.Excel;`, then one can do the following: `Excel.Application excelApp = null;` instead. – Tu deschizi eu inchid Jun 24 '22 at 15:08
  • @user9938 … Absolutely, this will simplify some of the coding and I only left it out as to keep it close to the OP's posted code. But as you suggested I would probably use EPPlus. Sometimes Interop is the only library you can use for one reason or another. Thanks for the good suggestion. – JohnG Jun 24 '22 at 15:23
  • This is great thanks, I have got this code to work so my excel file will populate the datagridview, however all of my columns appear twice, one empty and one with the data, why is this? – jacko10101 Jun 27 '22 at 14:28
  • I would have to see the code that produces these duplicate columns. Are you “manually” adding columns to the grid in the designer? In my code above, the columns are created by the data source. – JohnG Jun 27 '22 at 15:20