Unmerging excel rows, and duplicate data c# - c#

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;
}
}
}

Related

Manually adding values to charts in excel c#

I'm working with Spire.XLS licenced pack and I need to find a way to set the values for a chart manually from a c# win form application, speaking about category labels to actual values to display inside, instead of being forced to use sheet.Range[], or at least be able to use single cell values separated one from another from a sheet and use those like an "extended range" for the values any idea if this is possible?
Yes, it is possible. See the following example which shows how to create a chart without using worksheet data range.
using Spire.Xls;
namespace Create_chart
{
class Program
{
static void Main(string[] args)
{
//Create a workbook
Workbook wb = new Workbook();
//Get the first worksheet
Worksheet sheet = wb.Worksheets[0];
//Add a chart to the worksheet
Chart chart = sheet.Charts.Add();
//Add a series to the chart
var series = chart.Series.Add();
//Add data
series.EnteredDirectlyValues = new object[] { 10, 20, 30 };
//Save the file
wb.SaveToFile("result.xlsx", ExcelVersion.Version2013);
}
}
}
For more information, you can check this documentation: Create Chart without Using Worksheet Data Range in C#

Extra Excel Application Opening while creating powerpoint charts

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.

How to get the row number of the last row when you are printing an excel sheet (EPPlus)

I want to add one picture (displaying "DRAFT") by printable Excel worksheet in C# EPPlus.
I need to know if there is a way to find the last visible row of each page of a worksheet when you are printing it. I can't pretend that it will always be a fix number of row per page because it depends on the content of the cells.
Here is my current code that use a fix number of row per page (30) to insert image. This result in approximately one image per printable page except that in each new page the image is not at the same place. (Slightly off, depending on content of cells.)
public void InsertDraftImage(ExcelWorksheet worksheet, FileInfo draft_image)
{
int maxRowNumber = worksheet.Dimension.End.Row;
int rowByPage = 30;
int numberOfPage = (maxRowNumber / rowByPage) + 1;
ExcelPicture picture = null;
for(int i = 0; i < numberOfPage; i++)
{
if(draft_image != null)
{
picture = worksheet.Drawings.AddPicture(i.ToString(), draft_image);
picture.SetSize(609, 545); //original image size
picture.SetPosition(i * rowByPage, 0, 1, 0);
picture.EditAs = eEditAs.Absolute;
}
}
After trying to implement the missing code in 'ExcelHeaderFooter.cs' from the EPPlus with a workmate without success, we finally did it by following Ernie suggestion!!
There is my final code to insert a picture into each page of a printable excel file generate with EPPlus in C#.
It is done by adding the picture in the footer and setting the Boolean ScaleWithDoc to false (default = true).
public void InsertDraftImage(ExcelWorksheet worksheet, FileInfo draft_image)
{
ExcelHeaderFooterText footer = worksheet.HeaderFooter.OddFooter; //all page have same footer
footer.InsertPicture(draft_image, PictureAlignment.Centered);
}
Added this code in my method to create the ExcelWorksheet (all the other excel style, populate, settings).
XmlAttribute temp = worksheet.WorksheetXml.CreateAttribute("scaleWithDoc");
temp.Value = "0";
worksheet.WorksheetXml.GetElementsByTagName("headerFooter")[0].Attributes.Append(temp);
package.Save();

Improve performance of Excel file creation

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

Save a datatable to excel sheet in vb.net winform application

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

Categories