Here is my code...
Microsoft.Office.Interop.Excel.Application Excel = new Microsoft.Office.Interop.Excel.Application();
Workbook wb = Excel.Workbooks.Add(XlSheetType.xlWorksheet);
Worksheet ws = (Worksheet)Excel.ActiveSheet;
Excel.Visible = true;
ws.Cells[1, 1] = "ID";
ws.Cells[1, 2] = "Name";
ws.Cells[1, 3] = "Detail";
ws.Cells[1, 4] = "Category";
ws.Cells[1, 5] = "Brand";
ws.Cells[1, 6] = "In Stock";
ws.Cells[1, 7] = "Price";
ws.Cells[1, 8] = "Total";
for (int i = 2; i <= dgDetail.Rows.Count; i++)
{
for (int j = 2; j <= 8; j++)
{
ws.Cells[i, j] = dgDetail.Rows[i - 2].Cells[j - 1].Value;
}
}
Excel has 1,1 based arrays, the first cell is 1,1, so you did it right when you put "ID" to this cell. Your gridview looks zero-based.
1) You start to export dgDetail from row 0, which I think is right, but you skip one last row - you can rewrite your code a little bit to see it:
for (int i = 0; i <= dgDetail.Rows.Count-2; i++) {
dgDetail.Rows[i]; //pseudocode
}
Maybe it's how you planned it, but you don't export row dgDetail.Row.Count - 1
2)
for (int j = 2; j <= 8; j++) {
ws.Cells[i, j] = dgDetail.Rows[i - 2].Cells[j - 1].Value;
}
Here you need to change the first value of j to 1, because you start from second column otherwise:
ws.Cells[i, 2] = dgDetail.Rows[i - 2].Cells[2 - 1].Value;
I guess you are just confused by excel 1,1 based arrays a little bit,
Also, if you have big exports, it's better to write the whole array in one operation:
Range range = worksheet.Range[worksheet.Cells[1, 1], worksheet.Cells[2, 2]];
range.Value = new object[2, 2] { { 1, 2 }, { 3, 4 } };
Related
In a group project I have to write a table to an Excel sheet. This I have done but I need to highlight the table area where the data goes, as can be seen on these pictures:
https://gyazo.com/51c57897d9a1ce8df000d6ff0f18de20
https://gyazo.com/bcc879cd7d1c5f12ccb853490dca22f2
The first picture shows how it should look without data, and the second picture shows my current file with the data loaded.
Is it possible to highlight like it is seen in the first picture where the data is supposed to be?
I have not been able to find any sources dealing with this online.
Here is the code that creates the Excel sheet. Take note that I have not looked into formatting the code properly, but it should be readable:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ExcelLibrary.SpreadSheet;
using System.Data;
using System.IO;
using System.Windows;
using Excel = Microsoft.Office.Interop.Excel;
namespace ProjectXstaal
{
class Print
{
public void LoadToExcel(DataTable dt)
{
string filetest = "filetest.xlsx";
double allHours = 0;
double overtimeHours = 0;
if (File.Exists(filetest))
{
File.Delete(filetest);
}
Excel.Application oApp;
Excel.Worksheet oSheet;
Excel.Workbook oBook;
oApp = new Excel.Application();
oBook = oApp.Workbooks.Add();
Excel.Worksheet ws = (Excel.Worksheet)oApp.ActiveSheet;
// Calculates the total hours worked and total overtime hours. not important for the question
for (int i = 0; i < dt.Rows.Count; i++)
{
string overtimeTemp = dt.Rows[i][3].ToString();
string temp = dt.Rows[i][2].ToString();
double temp2 = Double.Parse(temp);
double temp3 = Double.Parse(overtimeTemp);
overtimeHours += temp3;
allHours += temp2 + temp3;
if (i >= 40)
{
break;
}
}
// some formatting of the page
ws.Cells[3, 1] = "Tidsperiode:";
ws.Cells[3, 5] = "NAVN";
ws.Cells[3, 6] = dt.Rows[1][7].ToString();
ws.Cells[1, 5] = DateTime.Now.Year.ToString();
ws.Cells[1, 3] = "ARBEJDSSEDDEL";
ws.Cells[40, 1] = "SAMMENLAGT FOR UGEN";
ws.Cells[40, 5] = allHours;
ws.Cells[41, 1] = "UGENS TIMER FORDELES PÅ";
ws.Cells[43, 5] = overtimeHours;
ws.Cells[42, 5] = allHours - overtimeHours;
ws.Cells[42, 4] = "NORMALTIMER";
ws.Cells[43, 4] = "OVERARBEJDE";
ws.Cells[6, 1] = dt.Columns[1].ToString();
ws.Cells[6, 2] = dt.Columns[5].ToString();
ws.Cells[6, 3] = dt.Columns[0].ToString();
ws.Cells[6, 4] = dt.Columns[4].ToString();
ws.Cells[6, 5] = dt.Columns[2].ToString();
ws.Cells[6, 6] = dt.Columns[3].ToString();
ws.Cells[6, 7] = dt.Columns[6].ToString();
//prints the datatable to the excel sheet and stops when it cant fit anymore information
for (int i = 0; i < dt.Rows.Count; i++)
{
ws.Cells[i + 7, 1] = dt.Rows[i][1].ToString();
ws.Cells[i + 7, 2] = dt.Rows[i][5].ToString();
ws.Cells[i + 7, 3] = dt.Rows[i][0].ToString();
ws.Cells[i + 7, 4] = dt.Rows[i][4].ToString();
ws.Cells[i + 7, 5] = dt.Rows[i][2].ToString();
ws.Cells[i + 7, 6] = dt.Rows[i][3].ToString();
ws.Cells[i + 7, 7] = dt.Rows[i][6].ToString();
ws.Cells[i + 7, 1].Bold = true;
if (i >= 32)
{
break;
}
}
// Sets column width of the data.
ws.Range["A6"].ColumnWidth = 11;
ws.Range["B6"].ColumnWidth = 10;
ws.Range["C6"].ColumnWidth = 6.7;
ws.Range["D6"].ColumnWidth = 11;
ws.Range["E6"].ColumnWidth = 11;
ws.Range["F6"].ColumnWidth = 15;
ws.Range["G6"].ColumnWidth = 15;
oBook.SaveAs(filetest);
oBook.Close();
oApp.Quit();
MessageBox.Show("Det virker måske");
}
}
}
If you now the range which should be bold, use:
Microsoft.Office.Interop.Excel.Range range = xlWorkSheet.get_Range("A1:A6","G1:G6");
range.Font.Bold = true;
In this example, the first 6 rows and the first 7 seven columns get bold, assuming the cell in the top-left corner is "A1".
I hope this helps!
I'm exporting a datagridview to Excel using a button's click event. The data is exporting but the last row of the table is not appearing. I've played with my code but I'm unable to figure out the problem. If someone would help me I would be grateful.
private void button34_Click(object sender, EventArgs e)
{
Microsoft.Office.Interop.Excel.Application Excel = new
Microsoft.Office.Interop.Excel.Application();
Workbook wb = Excel.Workbooks.Add(XlSheetType.xlWorksheet);
Worksheet ws = (Worksheet)Excel.ActiveSheet;
Excel.Visible = true;
ws.Cells[1, 1] = "VehiclePlateNumber";
ws.Cells[1, 2] = "VehicleDescription";
ws.Cells[1, 3] = "DeviceCode";
ws.Cells[1, 4] = "DriverName";
ws.Cells[1, 5] = "DriverKeyTag";
for (int j = 2; j <= datagridview9.Rows.Count; j++)
{
for (int i = 1; i <= 5; i++)
{
ws.Cells[j, i] = datagridview9.Rows[j - 2].Cells[i - 1].Value;
}
}
}
Please change it to following statement and check once
for (int j = 2; j <= datagridview9.Rows.Count+1; j++)
Excel.Application xlApp ;
Excel.Workbook xlWorkBook ;
Excel.Worksheet xlWorkSheet ;
object misValue = System.Reflection.Missing.Value;
xlApp = new Excel.Application();
xlWorkBook = xlApp.Workbooks.Add(misValue);
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
int i = 0;
int j = 0;
for (i = 0; i <= dataGridView1.RowCount - 1; i++)
{
for (j = 0; j <= dataGridView1.ColumnCount - 1; j++)
{
DataGridViewCell cell = dataGridView1[j, i];
xlWorkSheet.Cells[i + 1, j + 1] = cell.Value;
}
}...
As you can see above, I am exporting data from a datagridview to an .xls file. The problem is that when the file is exported, that the data which is is more than 10 numbers or so, appears to be notated in scientifically notation when the file is opened in Excel. This results in dataloss. And I can't permit that myself because the data is a code rather than a count.
if a Cell.Value can be interpreted as a number, Excel will interpret it as a number. Unless the Cell.NumberFormat is Text ("#").
Try
...
xlWorkSheet.Cells[i + 1, j + 1].NumberFormat = "#";
xlWorkSheet.Cells[i + 1, j + 1] = cell.Value
...
Greetings
Axel
I am working on some code that a developer I replaced wrote. He wrote a lengthy piece of code what writes to multiple excel worksheets on the same excel file. I am thinking about using several background workers to speed up the process of writing to four excel worksheets. Would there be a reason why it would be a good idea to leave all this on one thread? I have used multi-threading before, but not in c# and not writing to excel. I could not find any documentation either way.
Here is the code
xlWorkSheet = xlWorkBook.Worksheets.get_Item(1);
// work order
xlWorkSheet.Cells[4, 4] = nld.s_WorkOrderNumber;
// technician
xlWorkSheet.Cells[6, 4] = nld.s_TechnicianName;
// date and time
xlWorkSheet.Cells[4, 10] = (string)DateTime.Now.ToShortDateString();
xlWorkSheet.Cells[6, 10] = (string)DateTime.Now.ToShortTimeString();
row = 30;
col = 1;
// left connectors and part number
conCount = nld.n_LeftConnCount;
for (i = 0; i < conCount; i++)
{
xlWorkSheet.Cells[row, col] = "Name: " + nld.ConnDataLeft[i].s_ConnName + " PartNo: " + nld.ConnDataLeft[i].s_ConnPartNumber;
row++;
}
// Right connectors and part number
row = 30;
col = 7;
conCount = nld.n_RightConnCount;
for (i = 0; i < conCount; i++)
{
xlWorkSheet.Cells[row, col] = "Name: " + nld.ConnDataRight[i].s_ConnName + " PartNo: " + nld.ConnDataRight[i].s_ConnPartNumber;
row++;
}
// put down the pin map onNetlist worksheet
xlWorkSheet = xlWorkBook.Worksheets.get_Item(2);
row = 5;
col = 1;
i = 0;
leftPinNum = 0;
int connCount = nld.pinMap.Count;
for(i = 0; i < connCount; i++)
{
xlWorkSheet.Cells[row, col] = (i+1).ToString();
leftPinNum = nld.pinMap[i].pinLeft;
xlWorkSheet.Cells[row, col + 1] = nld.pinMap[i].conLeftName;
xlWorkSheet.Cells[row, col + 2] = nld.pinMap[i].pinLeftName;
xlWorkSheet.Cells[row, col + 4] = nld.pinMap[i].conRightName;
xlWorkSheet.Cells[row, col + 5] = nld.pinMap[i].pinRightName;
row++;
}
// put down the pin map onNetlist worksheet
xlWorkSheet = xlWorkBook.Worksheets.get_Item(3);
row = 5;
col = 1;
i = 0;
leftPinNum = 0;
for (i = 0; i < connCount; i++)
{
xlWorkSheet.Cells[row, col] = (i + 1).ToString();
leftPinNum = nld.pinMap[i].pinLeft;
xlWorkSheet.Cells[row, col + 1] = nld.pinMap[i].conLeftName;
xlWorkSheet.Cells[row, col + 2] = nld.pinMap[i].pinLeftName;
xlWorkSheet.Cells[row, col + 4] = nld.pinMap[i].conRightName;
xlWorkSheet.Cells[row, col + 5] = nld.pinMap[i].pinRightName;
if (facadeIntoNetList.ReturnIfUseShort(i))
{
xlWorkSheet.Cells[row, col + 7] = "True";
}
else
{
xlWorkSheet.Cells[row, col + 9] = "True";
}
row++;
}
// put down the pin map onNetlist worksheet
xlWorkSheet = xlWorkBook.Worksheets.get_Item(4);
row = 5;
col = 1;
i = 0;
leftPinNum = 0;
for (i = 0; i < connCount; i++)
{
xlWorkSheet.Cells[row, col] = (i + 1).ToString();
leftPinNum = nld.pinMap[i].pinLeft;
xlWorkSheet.Cells[row, col + 1] = nld.pinMap[i].conLeftName;
xlWorkSheet.Cells[row, col + 2] = nld.pinMap[i].pinLeftName;
xlWorkSheet.Cells[row, col + 6] = nld.pinMap[i].conRightName;
xlWorkSheet.Cells[row, col + 7] = nld.pinMap[i].pinRightName;
row++;
}
I know the temptation to do this: Those Office COM interfaces are painfully slow. But they also don't support multithreading at all. It's not a C# issue, it is an Excel+COM issue. If you need speed, then write an .xlsx using a 3rd-party library then launch Excel to open the file. That might literally be hundreds of times faster.
string workbookPath = filePath;
Excel.Application xlApp;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
Excel.Range range;
int rCnt = 0;
int cCnt = 0;
xlApp = new Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(workbookPath, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
range = xlWorkSheet.UsedRange;
int rowCount = range.Rows.Count;
int columnCount = range.Columns.Count;
string[][] myArray = new string[rowCount][];
for (rCnt = 1; rCnt < rowCount; rCnt++)
{
myArray[rCnt] = new string[columnCount];
for (cCnt = 1; cCnt < columnCount; cCnt++)
{
object str = (range.Cells[rCnt, cCnt] as Excel.Range).get_Value();
-----> myArray[rCnt][cCnt] = str == null ? string.Empty : str.ToString();
}
}
xlWorkBook.Close(true, null, null);
xlApp.Quit();
return myArray;
Above is the code for uploading an excel file to a sql database.
Essentially when I pass over the rCnt and cCnt variables where the ----> is above... the row count goes up once everytime the column count goes up twice ... I want to the column count to go up to 3, or however many usable columns there are.
The reason it's doing this could be that in the first row of the excel csv - there is nothing in the cell - however, in the 4-5th row, there is data in the 3rd column.
Could anyone glance at what I am missing?
Any questions / help would be greatly appreciated.
My first guess is that your code string[][] myArray = new string[rowCount][]; and myArray[rCnt] = new string[columnCount]; is allowing you to return a jagged array and you're going beyond those bounds somehow. UsedRange can sometimes be finicky (especially if you've deleted data - the range doesn't shrink down to fit your current data).
This code works fine for me, I use it a bit at work. COM entry/release code not included, I can add that later if you want it.
public string[,] ReadArray(int SheetNumber)
{
worksheet = (Excel.Worksheet)sheets.get_Item(SheetNumber);
Excel.Range range = worksheet.UsedRange;
//Get array, use range values. Not always correct, esp if user has deleted data. Ignore null values
Object[,] arr = range.Cells.Value;
string[,] output = new string[arr.GetLength(0), arr.GetLength(1)];
for (int i = 0; i < arr.GetLength(0); i++)
{
for (int j = 0; j < arr.GetLength(1); j++)
{
//If nothing in the cell, write a blank
if (arr[i + 1, j + 1] == null)
{
output[i, j] = "";
continue;
}
//else, write the output
output[i, j] = arr[i + 1, j + 1].ToString();
}
}
if (range != null)
Marshal.ReleaseComObject(range);
if (worksheet != null)
Marshal.ReleaseComObject(worksheet);
return output;
}