I am facing a strange phenomenon.
I wrote a method that copy values from datatable to an excel sheet.
The problem is that the rows are copied to columns and columns to rows.
For example:
DataTable:
Item Quantity UnitPrice TotalPrice
A 10 2 20
B 3 15 45
C 100 0.5 50
Excel Sheet:
Item Quantity UnitPrice TotalPrice
A B C
10 3 100
2 15 0.5
20 45 50
My code:
private void PopulateDataWorkSheet()
{
xl.Workbooks xlWorkbooks = _xlApp.Workbooks;
xl.Workbook xlWorkbook = xlWorkbooks.Open(_excelFileName);
xl.Sheets xlSheets = xlWorkbook.Sheets;
const int startRowIndex = 2; // first row for columns title
const int startColumnIndex = 1;
xl.Worksheet xlDataWorkSheet = xlSheets[DATA_SHEET];
xl.Range xlCells = xlDataWorkSheet.Cells;
for (var rowindex = 0; rowindex < _datatable.Rows.Count; rowindex++)
{
for (var columnindex = 0; columnindex < _datatable.Columns.Count; columnindex++)
{
xlCells[startRowIndex + rowindex][startColumnIndex + columnindex] = _datatable.Rows[rowindex][columnindex];
}
}
xlWorkbook.Save();
xlWorkbook.Close();
xlWorkbooks.Close();
ReleaseExcelObject(xlCells);
ReleaseExcelObject(xlDataWorkSheet);
ReleaseExcelObject(xlSheets);
ReleaseExcelObject(xlWorkbook);
ReleaseExcelObject(xlWorkbooks);
}
I can simply solve it by changing as following but I am not clear what is wrong:
xlCells[startColumnIndex + columnindex][startRowIndex + rowindex] = _datatable.Rows[rowindex][columnindex];
Did I miss something in my code?
I am facing a strange phenomenon. Did I miss something in my code?
Welcome to the world of Excel Range, dynamics, optional parameters and COM object Default member concept.
Excel Range is a strange thing. Almost every method/property returns another range. The indexer you used is something like this
dynamic this[object RowIndex, object ColumnIndex = Type.Missing] { get; set; }
i.e. has required row index and optional column index.
Let take this call
xlCells[startRowIndex + rowindex][startColumnIndex + columnindex] = ...;
it's equivalent to something like this
var range1 = (Excel.Range)xlCells[startRowIndex + rowindex];
var range2 = (Excel.Range)range1[startColumnIndex + columnindex];
// ^^^^^^
// this is treated as row index offset from the range1
range2.Value2 = ...;
Shortly, you need to change it to
xlCells[startRowIndex + rowindex, startColumnIndex + columnindex] = ...;
You need to change how you access your excel cells.
cells[StartRowIndex+ rowindex, StartColumnIndex + columnindex] is Row first and then column. What you do is cells[StartRowIndex + rowindex][StartColumnIndex + columnindex] which takes the column as a first argument.
The order of the loops does not matter at all.. so the right solution would be:
for (var rowindex = 0; rowindex < _datatable.Rows.Count; rowindex++)
{
for (var columnindex = 0; columnindex < _datatable.Columns.Count; columnindex++)
{
xlCells[startRowIndex + rowindex, startColumnIndex + columnindex] = _datatable.Rows[rowindex][columnindex];
}
}
Cheers!
You doing it wrong, you need to go like this:
for (var columnindex = 0; columnindex < _datatable.Columns.Count; columnindex++)
{
for (var rowindex = 0; rowindex < _datatable.Rows.Count; rowindex++)
{
xlCells[startRowIndex + rowindex][startColumnIndex + columnindex] = _datatable.Rows[rowindex][columnindex];
}
}
you need the column and then the row.
You can use following code to overcome the problem.It works fine for me.
xlApplication = new Microsoft.Office.Interop.Excel.Application();
// Create workbook .
xlWorkBook = xlApplication.Workbooks.Add(Type.Missing);
// Get active sheet from workbook
xlWorkSheet = xlWorkBook.ActiveSheet;
xlWorkSheet.Name = "Report";
xlWorkSheet.Columns.ColumnWidth = 30;
xlApplication.DisplayAlerts = false;
for (int rowIndex = 0; rowIndex < gridViewDataTable.Rows.Count; rowIndex++)
{
for (int colIndex = 0; colIndex < gridViewDataTable.Columns.Count; colIndex++)
{
if (rowIndex == 0)
{
xlWorkSheet.Cells[rowIndex + 1, colIndex + 1] = gridViewDataTable.Columns[colIndex].ColumnName;
xlWorkSheet.Cells[rowIndex + 1, colIndex + 1].Interior.Color = System.Drawing.Color.LightGray;
}
xlWorkSheet.Cells[rowIndex + 2, colIndex + 1].Borders.Color = Color.Black;
xlWorkSheet.Cells[rowIndex + 2, colIndex + 1].Interior.Color = System.Drawing.Color.White;
xlWorkSheet.Cells[rowIndex + 2, colIndex + 1] = gridViewDataTable.Rows[rowIndex][colIndex];
}
}
xlWorkBook.SaveAs(filename);
xlWorkBook.Close();
xlApplication.Quit();
Related
I am trying to get data from an Excel file. I know that indexing in excel starts from 1, but still when reading a simple matrix, I am getting an Index exceeds bounds error.
Here is a simple method I'm using:
public static string[,] ReadFromExcel(Worksheet excelSheet)
{
if (excelSheet == null) return new string[1,1];
Microsoft.Office.Interop.Excel.Range xlRange = excelSheet.UsedRange;
int firstRow = xlRange.Row;
int firstColumn = xlRange.Column;
int lastRow = xlRange.Row + xlRange.Rows.Count - 1;
int lastColumn = xlRange.Column + xlRange.Columns.Count - 1;
string[,] data = new string[xlRange.Rows.Count - xlRange.Row, xlRange.Columns.Count - xlRange.Column];
for (int i= 0; i<= lastRow - firstRow; i++)
{
for (int j= 0; j <= lastColumn - firstColumn; j++)
{
data[i,j] = (string)(excelSheet.Cells[firstRow + i][firstColumn + j] as Range).Value;
//When I try to read values I get an error
System.Windows.MessageBox.Show(data[i, j]);
}
}
return data;
}
What am I missing?
The size of your "data" array should match the number of rows & columns:
string[,] data = new string[xlRange.Rows.Count, xlRange.Columns.Count];
Although the start index for Excel Interop is 1, rather than zero, the count is correct & you don't need to subtract the start row & column.
I have a listbox and I manage to bind the Excel worksheet to the list box (after hours of researching on the Internet).
Now I need to read the values from the sheet into the listbox, but unable to locate any suitable solution.
This is what I have done so far:-
private void LoadExcelSheet(string path, int sheet){
_Application excel = new Excel.Application();
Workbook wb;
Worksheet ws;
int row = 0;
int col = 0;
wb = excel.Workbooks.Open(path);
ws = wb.Worksheets[sheet];
for (row = 1; row < 10; row++){
for (col = 1; col < 6; col++){
listBox1.Items.Add(ws.Cells[row, col].Value2);
}
}
}
//------------------ end of LoadExcelSheet---------------------------
this only display 1 row with each item of data on top of each other eg:-
aaaaaaaa
bbbbbbbb
cccccccc
dddddddd
instead of: aaaaaaaa bbbbbbbb cccccccc dddddddd
Thanks in advance..
The issue is with your for loops, try this:
for (var row = 1; row < 10; row++)
{
string r = "";
for (var col = 1; col < 6; col++)
{
r += " " + ws.Cells[row, col].Value2;
}
listBox1.Items.Add(r.Trim());
}
You are populating the list box inside a loop containing the columns, try something like
for (row = 1; row < 10; row++){
string a = "";
for (col = 1; col < 6; col++){
a += ws.Cells[row, col].Value2+" ";
}
listBox1.Items.Add(a);
}
Let's split the problem
Data collection
Collected data representation
Data collection (with a help of Linq):
using System.Linq;
...
string[] data = Enumerable
.Range(1, 9) // 9 rows, starting from 1
.Select(row => string.Join(" ", Enumerable // cols are joined with space
.Range(1, 5) // 5 columns in each row
.Select(col => ws.Cells[row, col].Value2)))
.ToArray();
And data representation (as ListBox items):
listBox1.Items.AddRange(data);
I have a set of codes to export grid view data to excel using EPPLUS. I am able to export the data to EPPLUS. But how do I export the colour of the font to excel as well? Can I set the dynamic coding for the font colour if ..... colour = green if ..... colour = red?
Thus I will need column4 to be coloured as well according to the value I set.
The below codes is how I set dynamically for gridview:
if (e.Row.RowType == DataControlRowType.DataRow)
{
TableCell cell = e.Row.Cells[16];
int Num = int.Parse(cell.Text);
if (Num >= 1&& Num <= 11)
{
cell.ForeColor = Color.Green;
}
if (Num >= 12&& Num <= 39)
{
cell.ForeColor = Color.Orange;
}
..........
}
The codes for my export:
protected void EXPORT_BUTTON_Click(object sender, EventArgs e)
{
ExcelPackage package = new ExcelPackage();
ExcelWorksheet Grid = package.Workbook.Worksheets.Add("ORSA ASSESSMENTS");
DataTable Gridview1 = new DataTable();
for (int i = 0; i < Gridview1.Columns.Count; i++)
{
Gridview1.Columns.Add("column" + i.ToString());
}
foreach (GridViewRow row in Gridview1.Rows)
{
DataRow dr = Gridview1.NewRow();
for (int j = 0; j < Gridview1.Columns.Count; j++)
{
row.Cells[j].Text = row.Cells[j].Text.Replace(" ", " ");
dr["column" + j.ToString()] = row.Cells[j].Text;
}
Gridview1.Rows.Add(dr);
}
Grid.Cells["A1"].LoadFromDataTable(Gridview1, true);
using (ExcelRange rng = Grid.Cells["A1:Z1"])
{
rng.Style.Font.Bold = true;
}
Grid.Cells[ORSA.Dimension.Address].AutoFitColumns();
var FolderPath = ServerName + DirectoryLocation + DirectoryFolder + ExportsFolder;
var filename = ExcelName + #"_" + ".xlsx";
var filepath = new FileInfo(Path.Combine(FolderPath, filename));
Response.Clear();
package.SaveAs(filepath);
Response.AddHeader("content-disposition", "attachment; filename=" + filename + ";");
Response.Charset = "";
Response.ContentType = "application/vnd.xlsx";
Response.TransmitFile(filepath.FullName);
Response.End();
}
Thus how can I do in my export codes so that I can export or set the forecolour dynamically upon exporting?
thanks
After this line
Grid.Cells["A1"].LoadFromDataTable(Gridview1, true);
Do a looping to loop the count of your DataTable
Example
protected void Page_Load(object sender, EventArgs e)
{
// Check
if (!IsPostBack)
{
DataTable dt = new DataTable();
// Create Column
for(int i = 0; i < 5; i++)
dt.Columns.Add("column" + i, typeof(int));
for (int i = 0; i < 10; i++)
dt.Rows.Add(i, i+1, i+2, i+3, i+4);
GenerateExcel(dt);
}
}
private void GenerateExcel(DataTable dt)
{
using (ExcelPackage pkg = new ExcelPackage())
{
ExcelWorksheet ws = pkg.Workbook.Worksheets.Add("Sheet1");
int num = 0;
string value = string.Empty;
ws.Cells[1, 1].LoadFromDataTable(dt, true);
// Get Your DataTable Count
int count = dt.Rows.Count;
int countCol = dt.Columns.Count;
bool isHeader = true;
for (int i = 0; i < count; i++ )
{
// Set Border
for (int j = 1; j <= countCol; j++)
{
// Set Border For Header. Run once only
if (isHeader) ws.Cells[1, j].Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Thin, System.Drawing.Color.Black);
ws.Cells[2 + i, j].Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Thin, System.Drawing.Color.Black);
}
// Set to false
isHeader = false;
// Cells 2 + i == Rows, Wondering start with 2 because your 1st rows is Title so 2 is the value
// Cells 2 == Column make sure your column is fix
value = ws.Cells[2 + i, 2].Value + ""; // You can use .ToString() if u sure is not null
// Parse
int.TryParse(value, out num);
// Check
if (num >= 1 && num <= 11)
ws.Cells[2 + i, 2].Style.Font.Color.SetColor(System.Drawing.Color.Green);
else if (num >= 12 && num <= 39)
ws.Cells[2 + i, 2].Style.Font.Color.SetColor(System.Drawing.Color.Orange);
}
pkg.SaveAs(new FileInfo("C:\\Test.xlsx"));
}
}
I have an Excel file with 2 columns [file in my pc]? Now iam getting 2 columns with 8 rows with below code.but continuously i should get 5*2=10 more of same columns with 8 rows in datagridview.
I have written the code which gives result only 2 columns but I should get 10 more columns with 8 rows.below is my code
for (int Cnum = 1; Cnum <= ShtRange.Columns.Count; Cnum++)
{
dt.Columns.Add((ShtRange.Cells[1, Cnum] as Excel.Range).Value2.ToString());
}
for (int Rnum = 2; Rnum <= 9; Rnum++)
{
dt.Rows.Add((ShtRange.Cells[Rnum, 1] as Excel.Range).Value2.ToString(),
(ShtRange.Cells[Rnum, 1] as Excel.Range).Value2.ToString());
}
}
for (int i = 0; i < 13; i++)
{
DataGridViewTextBoxCell txtbx = new DataGridViewTextBoxCell();
DataGridViewColumn txcol = new DataGridViewColumn();
txcol.CellTemplate = txtbx;
txcol.HeaderText = "column" + i.ToString();
dg1.Columns.Add(txcol);
}
dg1.Rows.Add(8);
int dgRw, dgCol;
dgRw = dgCol = 0;
foreach (DataRow dr in dt.Rows)
{
dg1[dgCol, dgRw].Value = dr[0].ToString();
dgRw++;
if (dgRw == 8)
{
dgCol++;
dgRw = 0;
}
I have a dataset that consists of 6 tables. When I place them into my method below, it works flawlessly on the first table. However, I notice in the second table the second row (under the columns) is blank and then the table data is shown. This is the same for table three only this time there are two rows which are blank. This increases the blank row as each remaining table is built. I verified that the tables themselves do not have these empty rows. I am hoping that someone here would be so kind to look at this code and let me know what would be the cause of this.
public Excel.Application PrepareForExport(System.Data.DataSet ds)
{
object missing = System.Reflection.Missing.Value;
Excel.Application excel = new Excel.Application();
Excel.Workbook workbook = excel.Workbooks.Add(missing);
int k = 0;
int tableCount = 6;
for (int j = 0; j < tableCount; j++)
{
Excel.Worksheet newWorksheet;
newWorksheet = (Excel.Worksheet)excel.Worksheets.Add(missing, missing, missing, missing);
System.Data.DataTable dt = new System.Data.DataTable();
dt = ds.Tables[j];
// name sheet
string tableName = string.Empty;
switch (j)
{
case 0:
tableName = "TEST1";
break;
case 1:
tableName = "TEST2";
break;
case 2:
tableName = "TEST3";
break;
case 3:
tableName = "TEST4";
break;
case 4:
tableName = "TEST5";
break;
case 5:
tableName = "TEST6";
break;
default:
tableName = "INVALID";
break;
}
newWorksheet.Name = tableName;
int iCol = 0;
foreach (DataColumn c in dt.Columns)
{
iCol++;
excel.Cells[1, iCol] = c.ColumnName;
}
int iRow = 0 + k;
foreach (DataRow r in dt.Rows)
{
iRow++;
for (int i = 1; i < dt.Columns.Count + 1; i++)
{
if (iRow == 1)
{
// Add the header the first time through
excel.Cells[iRow, i] = dt.Columns[i - 1].ColumnName;
}
if (r[1].ToString() != "")
{
excel.Cells[iRow + 1, i] = r[i - 1].ToString() + " - " + dt.Columns.Count;
}
}
}
k++;
}
return excel;
}
I think it's your int k variable and iRow that are the problems.
You set
int k = 0;
outside your looping through the tables.
Then inside the loop you have
int iRow = 0 + k;
and then you increment k at the end of the loop.
So for the first table you've got iRow = 0; second table, iRow = 1; third table, iRow = 2.