A year ago I saw a beautiful simple code that gets a data table and saves it in an excel file.
The trick was to use the web library (something with http) and I'm almost sure it was a stream.
I find a lot of code with response but I can't make it work in a win-form environment.
There is also a cell by cell code - not interested -too slow.
I want to paste it as a range or something close.
Thanks
I believe this is the code you're looking for:
DataTable to Excel
It uses an HtmlTextWriter.
There are many component libraries out there that will provide this kind of functionality.
However, you could probably, most simply output the data as a CSV file and the load that into Excel.
What I like to do is put the datatable in a grid allowing the user to sort and filter. Then they can use the clipboard to copy/paste to Excel.
Private Sub mnuCopy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuCopy.Click
If dgvDisplaySet.GetClipboardContent Is Nothing Then
MsgBox("Nothing selected to copy to clipboard.")
Else
Clipboard.SetDataObject(dgvDisplaySet.GetClipboardContent)
End If
End Sub
Thanks all especially Jay
my old code just as you suggested is:
at least the next time it will wait for me here ;)
private void cmdSaveToExcel_Click(object sender, EventArgs e)
{
saveFileDialog1.Filter = "Excel (*.xls)|*.xls";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
txtPath.Text = saveFileDialog1.FileName;
}
// create the DataGrid and perform the databinding
System.Web.UI.WebControls.DataGrid grid = new System.Web.UI.WebControls.DataGrid();
grid.HeaderStyle.Font.Bold = true;
if (connDBs != null && rtxtCode.Text != "")
{
DataTable dt;
dt = connDBs.userQuery(rtxtCode.Text); // getting a table with one column of the databases names
//grdData.DataSource = dt;
grid.DataSource = dt;
// grid.DataMember = data.Stats.TableName;
grid.DataBind();
// render the DataGrid control to a file
using (StreamWriter sw = new StreamWriter(txtPath.Text))
{
using (HtmlTextWriter hw = new HtmlTextWriter(sw))
{
grid.RenderControl(hw);
}
}
MessageBox.Show("The excel file was created successfully");
}
else
{
MessageBox.Show("Missing connection or query");
}
}
You need to convert your datatable into a ADO recordset, and then you can use the Range object's CopyFromRecordset method. See http://www.codeproject.com/KB/database/DataTableToRecordset.aspx
Related
I am creating charts in PowerPoint. The below code opens two excel applications. One opens in the background that is invisible. The second one opens after the method ends. I need to make sure second excel either never open ideally or I can close it after it opens.
I have tried the below things but none worked.
I have tried forcing GC, Manual ReleaseComObject, Killing Excel process
I have tried separating excel COM objects and forcing GC
private void BtnInsert_Click(object sender, EventArgs e)
{
var Addin = Globals.ThisAddIn;
Microsoft.Office.Interop.PowerPoint.Application activeApplication = Addin.Application;
DocumentWindow activeWindows = activeApplication.ActiveWindow;
Microsoft.Office.Interop.PowerPoint.View activeView = activeWindows.View;
Slide activeSlide = activeView.Slide;
Microsoft.Office.Interop.PowerPoint.Shapes slideShape = activeSlide.Shapes;
Microsoft.Office.Interop.PowerPoint.Shape shape = slideShape.AddChart2(-1, XlChartType.xl3DBarClustered, -1, -1, -1, -1, true);
Microsoft.Office.Interop.PowerPoint.Chart chart = shape.Chart;
//Access the chart data
Microsoft.Office.Interop.PowerPoint.ChartData chartData = chart.ChartData;
chartData.Activate();
//Create instance to Excel workbook to work with chart data
Workbook workbook = chartData.Workbook;
Microsoft.Office.Interop.Excel.Application workbookApplication = workbook.Application;
workbookApplication.Visible = false;
workbookApplication.WindowState = XlWindowState.xlMinimized;
//Accessing the data worksheet for chart
Worksheet worksheet = workbook.Worksheets[1];
// I am adding data here
// This is not required to reproduce this
chartData.BreakLink();
workbook.Close(true);
}
Also, note that this issue does not occur while updating data.
Remove chartData.Activate() and chartData.BreakLink() solves this.
Although online documentation says that chartdata.activate is required before accessing the workbook.
Otherwise, we will get a null reference.
I think the documentation is incorrect or it does not apply to vsto.
This image show what i basically want to do
I have plenty excel files that i need to prepare before inserting the data into a SQL database, one of the steps is unmerge excel cells and duplicate the data, i'm doing this doc parse with c#
I found a solution with VBA Macro Excel here
Sub UnMergeFill()
Dim cell As Range, joinedCells As Range
For Each cell In ThisWorkbook.ActiveSheet.UsedRange
If cell.MergeCells Then
Set joinedCells = cell.MergeArea
cell.MergeCells = False
joinedCells.Value = cell.Value
End If
Next
End Sub
But i need to do it on c# with microsoft.office.interop.excel
Does anyone know if there's a way to do this?
C# code is very similar:
private void UnMergeFill(Workbook wb)
{
foreach (Range cell in ((_Worksheet)wb.ActiveSheet).UsedRange)
{
if (cell.MergeCells)
{
var joinedCells = cell.MergeArea;
cell.MergeCells = false;
joinedCells.Value = cell.Value;
}
}
}
I have downloaded an application example source code, and I changed some of the code that loads Excel file into a DGV. The code is just a quick test and is not part of a productive project.
The code loads data from Excel file into a datagridview control using OLEDB. When the application first executes, it displays the window correctly (at this time no access to excel has taken place).
When the button that performs the load from Excel , to my surprise, when the OLEDB connection is established the currently displayed window is "automatically" re-sized as if you manually zoomed out (as you know, this is not a feature in winforms, but available on web when you press ctrl+shift+minus sign) from it. The data is loaded correctly to the DGV.
The code does not touch the scaling property or any window property at all.
I can't see any logical relationship between the two events. It just makes no sense to me at all.
Problem
I don't want this zooming to occur. I can't explain why it is hapening. Your suggestions are appreciated.
Platform
I am using VS2015 and Windows 10 on intel 64 machine
My attempts
I used debug to locate where the problem occurs. I performed other function like browsing using FileDialog control and it worked. I tried writing text to a label on the window which worked as expected. The problem only occurred after the connection is opened either explicitly or implicitly (by the data adapter)!
Code
Below I present part of the code (but I think it does not help!)
OleDbConnection objCon = null;
...
private void cmdReadExcel_Click(object sender, EventArgs e)
{
objDS = new DataSet();
string szConStr = collect_Con(System.IO.Path.GetExtension(txtFilePath.Text), txtFilePath.Text);
try
{
string dtName = "myExcelFile";
objCon = new OleDbConnection(szConStr);
//Added the following to ensure that no other code
//may have influenced these values
//These values are the default anyway
this.AutoSize = false;
this.AutoSizeMode = AutoSizeMode.GrowOnly;
//up to here code works as expected
objCon.Open();//*** Zoom out occurs after this line executes!!!!!!!
objDTExcel = new DataTable(dtName);
string sql =
#"select * from [Sheet1$]";
objDA = new System.Data.OleDb.OleDbDataAdapter(sql, objCon);
//*** zoom out occurs here if the connection is not explicity opened!!!
objDA.Fill(objDTExcel);
dataGridView1.DataSource = objDTExcel;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if (objDA != null)
{
objDA.Dispose();
objDA = null;
}
if (objCon != null)
{
objCon.Close();
objCon.Dispose();
objCon = null;
}
}
}
Sample images of 2 separate runs:
I have an application with a TabControl. It gets some TabPages, all having a DataGridView that gets filled with a DataTable.
Once the TabControl is filled, I want to be able to export all of the DataGridViews (or rather their DataSources, which are all DataTables) into one Excel file.
I have the following code for that. It works but takes almost a minute.
Button gets clicked:
private void exportBtn_Click(object sender, EventArgs e)
{
var result = new List<DataTable>();
foreach (TabPage page in tabControl1.TabPages)
{
var dgv = page.Controls[0] as DataGridView;
if (dgv == null) continue;
var dt = dgv.DataSource as DataTable;
if (dt == null) continue;
dt.TableName = page.Text;
result.Add(dt);
}
ExportServices.ToExcel(result);
}
ExportServices looks like this:
internal static class ExportServices
{
public static void ToExcel(List<DataTable> tables)
{
var excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Workbooks.Add();
foreach (var table in tables)
{
table.AddSheetToExcelApp(excelApp);
}
excelApp.Visible = true;
}
}
The extension method for DataTable, taken from this question:
public static void AddSheetToExcelApp(this DataTable Tbl, Microsoft.Office.Interop.Excel.Application excelApp)
{
try
{
if (Tbl == null || Tbl.Columns.Count == 0)
throw new Exception("ExportToExcel: Null or empty input table!\n");
// single worksheet
_Worksheet workSheet = (_Worksheet)excelApp.Sheets.Add();
workSheet.Name = Tbl.TableName.Remove(5,1);
// column headings
for (int i = 0; i < Tbl.Columns.Count; i++)
{
workSheet.Cells[1, (i + 1)] = Tbl.Columns[i].ColumnName;
}
// rows
for (int i = 0; i < Tbl.Rows.Count; i++)
{
for (int j = 0; j < Tbl.Columns.Count; j++)
{
workSheet.Cells[(i + 2), (j + 1)] = Tbl.Rows[i][j];
}
}
}
catch (Exception ex)
{
throw new Exception("ExportToExcel: \n" + ex.Message);
}
}
As I said, this code works. But it takes forever to do so. Pausing the execution of the program during random times showed me that most of the time it's doing work in the loop below // rows.
Any way to accelerate this? It would really be not much fun for the user to wait for a minute for just one Excel file.
EDIT: Forgot to mention I can't use any other libraries than the ones that I have installed. Our company is working with very sensitive data so we are not allowed to run any code from the "outside world".
To set cells one by one is very inefficient. I advise you to set the whole table at once:
object[,] array = new object[Tbl.Rows.Count,Tbl.Columns.Count]
// Fill the array with values then
workSheet.Range[workSheet.Cells[1, 1], workSheet.Cells[Tbl.Rows.Count, Tbl.Columns.Count]].Value = array;
Or, at least, to use bigger pieces.
It is normal that using COM dll to Excel it's take time to do this
Try this. You don't neeed excel on machine. I tested and I used this approach to export data to excel 1 000 000 rows
https://github.com/jsegarra1971/SejExcelExport
Full example how to use it:
http://www.codeproject.com/Tips/829389/Fast-Excel-Import-and-Export
I have used three methods over time:
1) write out a csv file, then import that into the excel sheet.
2) put the data into the clipboard, separating values with tabs, then pasting it into Excel (probably using paste special -> no formatting). Whether you can do this obviously depends a lot on the environment in which the programs are running.
3) learn about and use OpenXmlWriter. This is far more flexible than any of the other options, but also has quite a learning curve to get right.
You can generate the Excel file without executing Excel Application
You can use EPPlus library. It's a mature library with lots of functionality
I have this XML:
<container_1 attr1="..." attr2="..." attr3="...">
<rekord_1>
<type_1 attr1="..." attr2="..." ... />
<type_2 attr1="..." attr2="..." ... />
</rekord_1>
<rekord_2>... </rekord_2>
.
.
<rekord_N> ... </rekord_N>
</container_1>
I can parse the way I want and show it in a console app (using XMLElements, XMLNodeLists and a few loops) but I want to make an app with GUI and table view.
My form app could read the XML, but it can only handle 1 tag right now. For example, if I get the container_1 tag, it outputs only container_1's attributes by itself, but I want to show the records under the container_1 in table view also.
I want to make it view-able like my older program in console.
How can I get out the data from my XML so that I will be able to work with the elements/attributes? Because I want to set their place in the table view.
Which documentation should I read? Or is there some example which could be useful to me?
Edited(aug. 20):
Here is the main part of the code:
private void button1_Click(object sender, EventArgs e)
{
Stream input=null;
OpenFileDialog dialog = new OpenFileDialog();
openFileDialog1.InitialDirectory = "C:/";
openFileDialog1.Filter = "xml file | *.xml";
openFileDialog1.FilterIndex = 2;
openFileDialog1.RestoreDirectory = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK) {
try
{
if ((input = openFileDialog1.OpenFile()) != null)
{
using (input)
{
XmlReader xmlFile = XmlReader.Create(input, new XmlReaderSettings());
DataSet dataSet = new DataSet();
dataSet.ReadXml(xmlFile);
DataSet containers = new DataSet();
dataGridView1.DataSource = dataSet.Tables[tag];
xmlFile.Close();
}
}
}
catch (Exception ex) {
MessageBox.Show("ERROR");
}
}
}
This function does well the display if my tag is container or rekord and makes this output:
outputs
But I can't display container1+ its rekords,container2+its rekords and so on,only all the containers or all the rekords in same time.
sounds like what you want to do is pull the XML into a Dataset and then feed that DataSet to a Gridview.
and it looks like someone has already asked this question on Stack Exchange as well
Any way to populate items from xml to Datagridview using c#
Addition
what you have is a DataSet with multiple DataTables. the DataGridView is only able to display one DataTable at a time.
when you code
dataGridView1.DataSource = dataSet.Tables[tag];
and put container_1 in for tag you are pulling back the container_1 TABLE of your DataSet.
I am suggesting that you look at the MSDN for DataGridView, DataSet, and DataTable to see if this is really going to work for you.
maybe also check this out C# DataGridView Tutorial
it couldn't hurt.