I have a datatable and want it to export it to excel file, it is a wpf application and all the solutions that i have found are for web application asp.net please help...
just to make it better visible, for all
Microsoft.Office.Interop.Excel.Application excel = null;
Microsoft.Office.Interop.Excel.Workbook wb = null;
object missing = Type.Missing;
Microsoft.Office.Interop.Excel.Worksheet ws = null;
Microsoft.Office.Interop.Excel.Range rng = null;
try
{
excel = new Microsoft.Office.Interop.Excel.Application();
wb = excel.Workbooks.Add();
ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.ActiveSheet;
for (int Idx = 0; Idx < dt.Columns.Count; Idx++)
{
ws.Range["A1"].Offset[0, Idx].Value = dt.Columns[Idx].ColumnName;
}
for (int Idx = 0; Idx < dt.Rows.Count; Idx++)
{ // <small>hey! I did not invent this line of code,
// I found it somewhere on CodeProject.</small>
// <small>It works to add the whole row at once, pretty cool huh?</small>
ws.Range["A2"].Offset[Idx].Resize[1, dt.Columns.Count].Value =
dt.Rows[Idx].ItemArray;
}
excel.Visible = true;
wb.Activate();
}
catch (COMException ex)
{
MessageBox.Show("Error accessing Excel: " + ex.ToString());
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.ToString());
}
You can save a .csv(Comma Seperated Value File) from your datatable. This file can then be opened in Excel.
Moreover: Whether it is WPF or Winforms, converting is same in both because its conversion code is written in your language i.e. C# and is not specific to WPF or Winforms.
For me Work fine thank you ... some one like vb.net ?
Dim excel As Microsoft.Office.Interop.Excel.Application = Nothing
Dim wb As Microsoft.Office.Interop.Excel.Workbook = Nothing
Dim missing As Object = Type.Missing
Dim ws As Microsoft.Office.Interop.Excel.Worksheet = Nothing
Dim rng As Microsoft.Office.Interop.Excel.Range = Nothing
Sub ExcelFile(ByVal dt As DataTable)
Try
excel = New Microsoft.Office.Interop.Excel.Application()
wb = excel.Workbooks.Add()
ws = DirectCast(wb.ActiveSheet, Microsoft.Office.Interop.Excel.Worksheet)
For Idx As Integer = 0 To dt.Columns.Count - 1
ws.Range("A1").Offset(0, Idx).Value = dt.Columns(Idx).ColumnName
Next
For Idx As Integer = 0 To dt.Rows.Count - 1
' <small>hey! I did not invent this line of code,
' I found it somewhere on CodeProject.</small>
' <small>It works to add the whole row at once, pretty cool huh?</small>
' YES IT'S COOL Brother ...
ws.Range("A2").Offset(Idx).Resize(1, dt.Columns.Count).Value = dt.Rows(Idx).ItemArray
Next
excel.Visible = True
wb.Activate()
Catch ex As Exception
MessageBox.Show("Error accessing Excel: " & ex.ToString())
End Try
End Sub
one way
ArrayList arr = (ArrayList)dataGridView.DataSource;
dt = ArrayListToDataTable(arr);
dataTable2Excel(dt, dataGridView, pFullPath_toExport, nameSheet);
http://www.codeproject.com/Articles/30169/Excel-export-from-DatagridView
http://support.microsoft.com/default.aspx?scid=kb;en-us;317719
Related
I have an existing Excel sheet which has headers. I get data from my server and place it in my WPF DataGrid and it looks like this:
On a click of a button, I need to place the values from my list to a particular sheet in my existing Excel workbook. I can actually get the values from a WINFORM DataGrid like this:
var xlApp = new Excel.Application();
Excel.Worksheet sheet = new Excel.Worksheet();
xlApp.Visible = true;
var path = #"D:\Reports\Tag_History.xlsx";
sheet = xlApp.Application.Workbooks.Open(path).Worksheets["Summary"];
var rowCount = dataGrid.Items.Count;
var rowColumn = dataGrid.Columns.Count;
for (int i = 0; i < rowCount - 1; i++)
{
for (int j = 0; j < 7; j++)
{
if (dataGrid[j, i].ValueType == typeof(string))
{
xlsht.Cells[i + 2, j + 1] = "'" + dataGrid[j, i].Value.ToString();
}
else
{
xlsht.Cells[i + 2, j + 1] = dataGrid[j, i].Value.ToString();
}
}
}
but since I am trying to do this in WPF, this code does not work anymore. This is by transferring dataGrid data to an existing excel file. Since I think that transferring list to an existing excel file is better, I have to try this. This is what I have so far:
var xlApp = new Excel.Application();
Excel.Worksheet sheet = new Excel.Worksheet();
xlApp.Visible = true;
var path = #"D:\Reports\Tag_History.xlsx";
sheet = xlApp.Application.Workbooks.Open(path).Worksheets["Summary"];
var range = sheet.Range["A2", "A2"];
foreach (var item in summaryList)
{
range.Value2 = item.TagNumber;
}
This code works but it is only updating a single cell of the excel file.
Can you please show me how to do this? Thank you.
Install Microsoft.Office.Interop.Excel Nuget package in your application. Right-click on your project -> "References" and choose "Manage NuGet Packages...", then just search for Excel. Otherwise, select Tools -> Nuget Package Manager -> Package Manager Console -> then install the Excel nuget (https://www.nuget.org/packages/Microsoft.Office.Interop.Excel/).
Bind the items in DataGrid and then export data to excel as like below,
private void btnExport_Click(object sender, RoutedEventArgs e)
{
Microsoft.Office.Interop.Excel.Application excel = null;
Microsoft.Office.Interop.Excel.Workbook wb = null;
object missing = Type.Missing;
Microsoft.Office.Interop.Excel.Worksheet ws = null;
Microsoft.Office.Interop.Excel.Range rng = null;
// collection of DataGrid Items
var dtExcelDataTable = ExcelTimeReport(txtFrmDte.Text, txtToDte.Text, strCondition);
excel = new Microsoft.Office.Interop.Excel.Application();
wb = excel.Workbooks.Add();
ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.ActiveSheet;
ws.Columns.AutoFit();
ws.Columns.EntireColumn.ColumnWidth = 25;
// Header row
for (int Idx = 0; Idx < dtExcelDataTable.Columns.Count; Idx++)
{
ws.Range["A1"].Offset[0, Idx].Value = dtExcelDataTable.Columns[Idx].ColumnName;
}
// Data Rows
for (int Idx = 0; Idx < dtExcelDataTable.Rows.Count; Idx++)
{
ws.Range["A2"].Offset[Idx].Resize[1, dtExcelDataTable.Columns.Count].Value = dtExcelDataTable.Rows[Idx].ItemArray;
}
excel.Visible = true;
wb.Activate();
wb.SaveCopyAs("excel file location");
wb.Saved = true;
excel.Quit();
}
I working with windows application and processing large excel file. I need to save 100k rows from datatable to excel file.
Currently my create excel function only support 65,500 rows only?
But I need to save excel file more than that. Is it possible?
If yes then, Kindly give the source?
Here is my code
public static void ExportDataSetToExcel(DataTable dt, int index, string strFilePathName)
{
Console.WriteLine("Creating Output Excel file");
string fileFomat = getExcelFileName(index) + (DateTime.Now.ToString("yyyyMMddTHHmmss"));
Microsoft.Office.Interop.Excel.Application objXL = null;
Microsoft.Office.Interop.Excel.Workbook objWB = null;
try
{
objXL = new Microsoft.Office.Interop.Excel.Application();
objWB = objXL.Workbooks.Add(1);
int sheetcount = 1;
Microsoft.Office.Interop.Excel.Worksheet objSHT = (Microsoft.Office.Interop.Excel.Worksheet)objWB.Sheets.Add();
Microsoft.Office.Interop.Excel.Range cells = objSHT.Cells;
cells.NumberFormat = "#";
//formatRange = objSHT.get_Range("b1",Type.Missing);
//formatRange.EntireRow.Font.Bold = true;
objSHT.Name = "RunOrderSheet";
for (int j = 0; j < dt.Rows.Count; j++)
{
for (int i = 0; i < dt.Columns.Count; i++)
{
//Condition to put column names in 1st row
//Excel work book indexes start from 1,1 and not 0,0
if (j == 0)
{
objSHT.Cells[1, i + 1] = dt.Columns[i].ColumnName.ToString();
}
//Writing down data
objSHT.Cells[j + 2, i + 1] = dt.Rows[j][i].ToString();
}
}
sheetcount++;
objWB.Saved = true;
objWB.SaveCopyAs(strFilePathName.Trim() + fileFomat.Trim() + ".xlsx");
objWB.Close();
objXL.Quit();
Console.WriteLine("Process done");
}
catch (Exception ex)
{
objWB.Saved = true;
objWB.Close();
objXL.Quit();
log.Error(ex.Message);
}
}
Excel and the COM interface with C# absolutely will support more than 65k lines. Just to prove that it's possible, run the following code:
public static void ExportDataSetToExcel()
{
Excel.Application objXL = null;
Excel.Workbook objWB = null;
objXL = new Microsoft.Office.Interop.Excel.Application();
objXL.Visible = true;
objWB = objXL.Workbooks.Add(1);
Excel.Worksheet objSHT = objWB.Sheets.Add();
for (int row = 1; row < 100000; row++)
{
objSHT.Cells[row, 1].Value2 = row;
}
objWB.SaveAs("c:\test\test.xlsx", Excel.XlFileFormat.xlOpenXMLWorkbook);
objWB.Close();
objXL.Quit();
}
Which leads me to several possibilities as to what your issue might be, in order of my suspicions (first being what I think is the most likely):
Since you have a try/catch block that is very nicely trapping errors and making sure that anything that happens within your loop while still saving the file, something is tripping the proverbial circuit breaker. While I don't see any glaring errors, it could be anything. Put a breakpoint within the catch block and see what ex contains. Also, make note of the specific record that is causing the error so you can step through just that one record and see where it occurs.
I noticed you are using SaveCopyAs rather than SaveAs. SaveCopyAs automatically saves in the exact same format as the original. I would think your Excel comes up with the default xlsx, but I certainly can't guarantee that. Using SaveAs with the explicit file format will guarantee it is saved in a format that supports more than 65k lines:
.
objWB.SaveAs("c:\test\test.xlsx", Excel.XlFileFormat.xlOpenXMLWorkbook);
Without knowing how large your datatable is, it might simply be a memory issue also that's tripping the try/catch.
I'm saving links to excel file, but once i saved it the excel process is still runnning.
How i should relase this excel com object?
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook book = app.Workbooks.Add(1);
Microsoft.Office.Interop.Excel.Worksheet sheet = (Microsoft.Office.Interop.Excel.Worksheet)book.Worksheets[1];
for (int i = 0; i < Links.Count; i++)
{
sheet.Cells[i + 1, 1] = Links[i];
}
book.SaveAs("test.xlsx");
Marshal.ReleaseComObject(sheet);
sheet = null;
book.Close(true, null, null);
Marshal.ReleaseComObject(book);
book = null;
app.Quit();
Marshal.ReleaseComObject(app);
app = null;
Maybe "sheet.Cells" is a reference that you have to release:
var cells = sheet.Cells;
...
Marshal.ReleaseComObject(cells);
My code is as follows
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(file);
Excel.Worksheet xlSheet = xlWorkbook.Sheets[1]; // get first sheet
Excel.Range xlRange = xlSheet.UsedRange;
These are the only variables used in my function
foreach (Excel.Worksheet XLws in xlWorkbook.Worksheets)
{
// do some stuff
xlApp.UserControl = false;
if (xlRange != null)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlRange);
if (xlSheet != null)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlSheet);
if (xlWorkbook != null)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkbook);
xlRange = null;
xlSheet = null;
xlWorkbook = null;
xlApp.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlApp);
}
But still I get EXCEL.EXE in Task Manager
Please help?
Kill the excel process which has empty value for MainWindowTitle. Below is an example source code.
Microsoft.Office.Interop.Excel.Application oXL;
Microsoft.Office.Interop.Excel._Workbook oWB;
Microsoft.Office.Interop.Excel._Worksheet oSheet;
Microsoft.Office.Interop.Excel.Range oRng;
object misvalue = System.Reflection.Missing.Value;
try
{
//Start Excel and get Application object.
oXL = new Microsoft.Office.Interop.Excel.Application();
oXL.Visible = true;
//Get a new workbook.
oWB = (Microsoft.Office.Interop.Excel._Workbook)(oXL.Workbooks.Add(""));
oSheet = (Microsoft.Office.Interop.Excel._Worksheet)oWB.ActiveSheet;
//Add table headers going cell by cell.
oSheet.Cells[1, 1] = "First Name";
oSheet.Cells[1, 2] = "Last Name";
oSheet.Cells[1, 3] = "Full Name";
oSheet.Cells[1, 4] = "Salary";
//Format A1:D1 as bold, vertical alignment = center.
oSheet.get_Range("A1", "D1").Font.Bold = true;
oSheet.get_Range("A1", "D1").VerticalAlignment =
Microsoft.Office.Interop.Excel.XlVAlign.xlVAlignCenter;
// Create an array to multiple values at once.
string[,] saNames = new string[5, 2];
saNames[0, 0] = "John";
saNames[0, 1] = "Smith";
saNames[1, 0] = "Tom";
saNames[4, 1] = "Johnson";
//Fill A2:B6 with an array of values (First and Last Names).
oSheet.get_Range("A2", "B6").Value2 = saNames;
//Fill C2:C6 with a relative formula (=A2 & " " & B2).
oRng = oSheet.get_Range("C2", "C6");
oRng.Formula = "=A2 & \" \" & B2";
//Fill D2:D6 with a formula(=RAND()*100000) and apply format.
oRng = oSheet.get_Range("D2", "D6");
oRng.Formula = "=RAND()*100000";
oRng.NumberFormat = "$0.00";
//AutoFit columns A:D.
oRng = oSheet.get_Range("A1", "D1");
oRng.EntireColumn.AutoFit();
oXL.Visible = false;
oXL.UserControl = false;
oWB.SaveAs("c:\\test505.xls", Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookDefault, Type.Missing, Type.Missing,
false, false, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
oWB.Close(null, null, null);
oXL.Quit(); //MainWindowTitle will become empty afer being close
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oXL);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oWB);
Process[] excelProcesses = Process.GetProcessesByName("excel");
foreach (Process p in excelProcesses)
{
if (string.IsNullOrEmpty(p.MainWindowTitle)) // use MainWindowTitle to distinguish this excel process with other excel processes
{
p.Kill();
}
}
}
catch (Exception ex2)
{
}
You've got an implicit object left open. Try this
Excel.Application xlApp = new Excel.Application();
Excel.Workbooks xlWorkbooks = xlApp.Workbooks;
Excel.Workbook xlWorkbook = xlWorkbooks.Open(file);
....
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlApp);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkbooks);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkbook);
....
OK... I hope this helps... it took me forever to tweak this to get it to work just so...
Here is my entire function (VB -- but the C# code for the tricky stuff is in there (thanks to too many other stackoverflow giants who helped me get this far!)
Private Function ImportWorksFile() As Integer
Dim EndofSheet As Boolean
Dim BlankRowCounter As Integer
Dim rr As RowResult
Dim SecCount As Integer = 0
Dim SecRow As SecurityRow
Dim uf As New UtilFunctions
'If this has already been run, the instance of the excel object would have been 'killed' and needs to be reinstantiated
If blnExcelProcessKilled Then 'Global boolean var
xlApp = New Excel.Application()
blnExcelProcessKilled = False
End If
Dim excelProcess(0) As Process
excelProcess = Process.GetProcessesByName("excel")
Dim tmp As Excel.Workbooks
Try
tmp = xlApp.Workbooks
xlWorkBook = tmp.Open(WorkingFileName)
Catch ex As Exception
MessageBox.Show("There was a problem opening the workbook - please try again", CurAFLApp.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Return 0
End Try
Using dc As New AFLData(CurAFLApp, True)
Dim cmd As SqlCommand = DefineCommand()
cmd.CommandType = CommandType.StoredProcedure
For Each ws As Excel.Worksheet In xlWorkBook.Worksheets
Dim row As Integer = 1
EndofSheet = False
BlankRowCounter = 0
If ImpCols.ContainsKey(ws.Name) Then
SecRow = New SecurityRow(ImpCols(ws.Name))
Do Until EndofSheet
Try
SecRow.NewRow(ws.Rows(row))
rr = SecRow.IsValidRow
If rr = RowResult.Valid Then
' read this row and process
With cmd
.Parameters("#AcctDate").Value = FileDate
.Parameters("#NewSub").Value = SecRow.GetStrCell("newsub")
RunProcedure(cmd)
End With
SecCount += 1
BlankRowCounter = 0
Else
BlankRowCounter += rr
End If
Catch ex As Exception
MessageBox.Show("There was a problem with row: " & row & " in workbook " & ws.Name)
End Try
' if we've counted 50 blank A column values in a row, we're done.
If BlankRowCounter <= -50 Then
EndofSheet = True
End If
row += 1
Loop
End If
Next
End Using
Try
xlWorkBook.Close(SaveChanges:=False)
xlApp.Workbooks.Close()
xlApp.Quit()
'// And now kill the process. C# Version (for reference)
'if (processID != 0)
'{
' Process process = Process.GetProcessById(processID);
' process.Kill();
'}
' Reversed the order of release per http://stackoverflow.com/questions/12916137/best-way-to-release-excel-interop-com-object
Catch ex As Exception
MessageBox.Show("There was a problem CLOSING the workbook - Please double check that the data was imported correctly. ", CurAFLApp.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Return 0
Finally
releaseObject(tmp)
releaseObject(xlWorkBook)
releaseObject(xlApp)
If Not excelProcess(0).CloseMainWindow() Then
excelProcess(0).Kill()
blnExcelProcessKilled = True
End If
End Try
Return SecCount
End Function
Public Sub releaseObject(ByVal obj As Object)
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
obj = Nothing
Catch ex As Exception
obj = Nothing
Finally
GC.Collect()
'Not sure if the following line helps or hinders -- seems to lock things up once in a while
'GC.WaitForPendingFinalizers()
End Try
End Sub
Try :
xlWorkbook.Close(false); // if you Workbook should not be saved
instead of :
if (xlWorkbook != null)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkbook);
xlWorkbook = null;
I am in the middle of simple method, that saves my DataGridView into an Excel document (1 sheet only) and also adds VBA code and a button to run the VBA code.
public void SaveFile(string filePath)
{
Microsoft.Office.Interop.Excel.ApplicationClass ExcelApp = new Microsoft.Office.Interop.Excel.ApplicationClass();
ExcelApp.Application.Workbooks.Add(Type.Missing);
//Change Workbook-properties.
ExcelApp.Columns.ColumnWidth = 20;
// Storing header part in Excel.
for (int i = 1; i < gridData.Columns.Count + 1; i++)
{
ExcelApp.Cells[1, i] = gridData.Columns[i - 1].HeaderText;
}
//Storing Each row and column value to excel sheet
for (int row = 0; row < gridData.Rows.Count; row++)
{
gridData.Rows[row].Cells[0].Value = "Makro";
for (int column = 0; column < gridData.Columns.Count; column++)
{
ExcelApp.Cells[row + 2, column + 1] = gridData.Rows[row].Cells[column].Value.ToString();
}
}
ExcelApp.ActiveWorkbook.SaveCopyAs(filePath);
ExcelApp.ActiveWorkbook.Saved = true;
ExcelApp.Quit();
}
I only implemented DataGridView export.
EDIT: Thanks to Joel I could, with proper words, search again for the solution. I think that this may be helpful. Would you correct me or give a tip or two about what I should look for.
I just wrote a small example which adds a new button to an existing workbook and afterwards add a macro which will be called when the button is clicked.
using Excel = Microsoft.Office.Interop.Excel;
using VBIDE = Microsoft.Vbe.Interop;
...
private static void excelAddButtonWithVBA()
{
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlBook = xlApp.Workbooks.Open(#"PATH_TO_EXCEL_FILE");
Excel.Worksheet wrkSheet = xlBook.Worksheets[1];
Excel.Range range;
try
{
//set range for insert cell
range = wrkSheet.get_Range("A1:A1");
//insert the dropdown into the cell
Excel.Buttons xlButtons = wrkSheet.Buttons();
Excel.Button xlButton = xlButtons.Add((double)range.Left, (double)range.Top, (double)range.Width, (double)range.Height);
//set the name of the new button
xlButton.Name = "btnDoSomething";
xlButton.Text = "Click me!";
xlButton.OnAction = "btnDoSomething_Click";
buttonMacro(xlButton.Name, xlApp, xlBook, wrkSheet);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
xlApp.Visible = true;
}
And here we got the buttonMacro(..) method
private static void buttonMacro(string buttonName, Excel.Application xlApp, Excel.Workbook wrkBook, Excel.Worksheet wrkSheet)
{
StringBuilder sb;
VBIDE.VBComponent xlModule;
VBIDE.VBProject prj;
prj = wrkBook.VBProject;
sb = new StringBuilder();
// build string with module code
sb.Append("Sub " + buttonName + "_Click()" + "\n");
sb.Append("\t" + "msgbox \"" + buttonName + "\"\n"); // add your custom vba code here
sb.Append("End Sub");
// set an object for the new module to create
xlModule = wrkBook.VBProject.VBComponents.Add(VBIDE.vbext_ComponentType.vbext_ct_StdModule);
// add the macro to the spreadsheet
xlModule.CodeModule.AddFromString(sb.ToString());
}
Found this information within an KB article How To Create an Excel Macro by Using Automation from Visual C# .NET