I am using Microsoft.Office.Interop.Excel to read an Excel file.
The problem I am having is that when reading, it counts the first row that has information as row 1. So, if the first row to have information is row 3, it counts that as the first row. This is a problem because the Excel sheets sometimes have data on the first 2 rows, sometimes they don't. But the data I need to read always starts on the third row.
Here is the routine I am using to read the sheets:
Microsoft.Office.Interop.Excel.Application exl = new Microsoft.Office.Interop.Excel.Application();
Workbooks wkbs = exl.Workbooks;
Microsoft.Office.Interop.Excel.Workbook wkb = null;
Sheets shts;
Microsoft.Office.Interop.Excel.Worksheet wks = null;
// load work book
//wkb = wkbs.Open(races_data_path, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);//oMissing
wkb = wkbs.Open(races_data_path, Type.Missing, true, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);//oMissing
shts = wkb.Sheets;
wks = (Microsoft.Office.Interop.Excel.Worksheet)shts.get_Item(Properties.Settings.Default.excel_worksheet);
// read lines from worksheet
Microsoft.Office.Interop.Excel.Range range = wks.UsedRange;
for (int rCnt = 1; rCnt <= range.Rows.Count; rCnt++)
{
try
{
object[,] values = (object[,])range.Value2;
if (Convert.ToString(values[rCnt, 4]) != "")
{
//Console.WriteLine(String.Format("Ldd: {0} {1} {2}", Convert.ToInt16(values[rCnt, 2]), Convert.ToInt16(values[rCnt, 3]), Convert.ToString(values[rCnt, 4])));
if (races[Convert.ToInt16(values[rCnt, 1])] == null) races[Convert.ToInt16(values[rCnt, 1])] = new RaceObject();
races[Convert.ToInt16(values[rCnt, 1])].add_racer(Convert.ToInt16(values[rCnt, 2]), Convert.ToInt16(values[rCnt, 3]), Convert.ToString(values[rCnt, 4]), Convert.ToString(values[rCnt, 7]));
}
}
catch (Exception ex)
{
data_loaded = false;
rCnt = range.Rows.Count;
}
}
This example, you can see the loop count starts at '1'. This was for reading a sheet where the first 2 lines of the sheet were empty. If the first 2 lines have data, then this loop only works if I change the count start to '3'.
For example:
The start value = 1 if the sheet looks like this:
no data
no data
has data
has data
has data
The start value = 3 if the sheet looks like this:
has unnecessary data
has unnecessary data
has data
has data
has data
Option 1: Add something to the first cell immediately before doing the rest: wks.cells(1, 1).value = "'" & wks.cells(1, 1).value This will extend the usedRange to the first cell.
Then the data will always start on the third row and you can easily always skip the first two rows.
Option 2: You can also use wks.UsedRange.Cells(1, 1).Row to see whether the first row is 1 or 3. Then you can adjust whether you use those rows or not.
(Edited post based on comments)
Related
I have the following code that based on the found value in an excel i have to update the other cell values in that row. But it is not updating the values back to the excel sheet. MasterTable is of DataTable which has the same column values as the excel i need to update these values to the excel. please help me find the issue.
for (int row = 0; row < masterTable.Rows.Count; row++)
{
// Get the CIS ID
string CISid = Convert.ToString(masterTable.Rows[row][RemoveSpecialCharachtersAndSpace("SOW / CIS No")]);
Excel.Range range = xlWorksheet.UsedRange.Rows.Find(CISid, System.Type.Missing, Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, System.Type.Missing, System.Type.Missing, System.Type.Missing);
//FindRangeCISByID(CISid);
if (range != null && range.Rows.Count > 0 && (!string.IsNullOrEmpty(Convert.ToString(range.Rows.Cells[1, 1].Value2))))
{
for (int cell1 = 1; cell1 < 5; cell1++)
{
//Master Excel Copy
string masterColumnCopy = Convert.ToString(range.Rows.Cells[1, cell1].Value2);
// Local Data table copy of column value
string datatableColumnValue = GetColumnValueFromDataTable(masterTable, cell1);
xlWorksheet.UsedRange.Find(CISid).Cells[1, cell1].Value = datatableColumnValue ;
}
}
}
xlWorkbook.Save();
xlWorkbook.Close();
xlApp.Quit();`
As odd it sounds , but it is working with saveAs function for work book not with the save.
I have changed the save code to the following
xlWorkbook.SaveAs(#"D:\ExcelData_new.xlsx", 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);
and it is working
I am reading an Excel sheet programmatically using Microsoft.Office.Interop.Excel in C#.
I am able to read it row by row and converting each row to a string arrray. Then, I am adding these rows to a DataTable.
Every thing works fine except the one of the column in the Excel contains Date values, and when I fetch it from the Excel Range object and cast it to string array, the date values gets converted to some sort of decimal numbers.
For e.g.-
If the date value is '6/4/2016 8:14:39 PM', I get the value as '42522.5224305556'
If the date value is '5/27/2016 1:10:12 PM', I get the value as '42517.54875'
Below is my code-
private System.Data.DataTable GetTicketsFromExcel(string excelFilePath)
{
System.Data.DataTable dtblTickets = new System.Data.DataTable();
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Worksheet ws = new Worksheet();
Workbook wb = null;
try
{
wb = excelApp.Workbooks.Open(excelFilePath, Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.Sheets.get_Item(1);
Range usedRange = ws.UsedRange;
Range rowRange;
string[] lsRow = null;
for (int i = 1; i <= usedRange.Columns.Count; i++)
{
dtblTickets.Columns.Add(usedRange.Cells[5, i].Value.ToString());
}
string sortColumn = "Reported On";
string sortDirection = "DESC";
dtblTickets.Columns[sortColumn].DataType = typeof(DateTime);
for (int row = 6; row <= usedRange.Rows.Count; row++)
{
//dtblTickets.Columns.Add()
rowRange = usedRange.Rows[row];
object[,] cellValues = (object[,])rowRange.Value2;
lsRow = cellValues.Cast<object>().Select(o => Convert.ToString(o)).ToArray<string>();
dtblTickets.Rows.Add(lsRow.ToArray());
}
dtblTickets.DefaultView.Sort = sortColumn + " " + sortDirection;
dtblTickets = dtblTickets.DefaultView.ToTable();
}
catch (Exception ex)
{
}
finally
{
wb.Close();
excelApp.Quit();
Marshal.ReleaseComObject(ws);
Marshal.ReleaseComObject(wb);
Marshal.ReleaseComObject(excelApp);
ws = null;
wb = null;
excelApp = null;
}
return dtblTickets;
}
Please note-
I don't want to use OLEDB to read and export this
I want to able to read the Excel row by row (without extracting each cell value and converting them)
I don't want to convert/format the original Excel document data
Can someone please help me with this?
Not quite sure, if you want to solve the problem this way, but one way is to change the property of the Cells (or the whole row or column) in Excel.
Right click on a Cell
Format Cells
Under "Number" select Category "Text" for the Cells.
I've tested it and it worked.
I like to delete rows from multiple worksheet in Excel. Currently my code only delete rows from active worksheet. I am looking for a specific value on the cell then when I find this value then I am deleting all the rows up to that value that stored on that row.
Code
private void button2_Click(object sender, EventArgs e)
{
Microsoft.Office.Interop.Excel.Application Excel = new Microsoft.Office.Interop.Excel.Application();
Workbook workBook = Excel.Workbooks.Open(FilePath);
Worksheet ws = (Worksheet)Excel.ActiveSheet;
for (int j = 1; j <= 10; j++)
{
for (int i = 1; i <= 20; i++)
{
if (Convert.ToString(((Microsoft.Office.Interop.Excel.Range)ws.Cells[j, i]).Value2) == "Matter")
{
for (int r = 1; r <= j; r++)
{
((Range)ws.Rows[r]).Delete(XlDeleteShiftDirection.xlShiftUp);
MessageBox.Show(Convert.ToString(r));
workBook.SaveAs(#"C:\Users\Separate\New.xlsx", 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);
}
}
}
}
workBook.Close(Type.Missing, Type.Missing, Type.Missing);
Excel.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(Excel);
Excel = null;
}
I would highly suggest trying to use ClosedXML as it makes working with Excel so much easier. Specifically with ClosedXML you can have a line like this:
ws.Row(5).Delete();
where ws is your initialized worksheet, and it handles the heavy lifting for deleting that row.
Getting or setting cell data is just as simple:
ws.Cell(2, 2).Value = "Initial Value";
or alternatively
ws.Cell("B2").Value = "Value;
They also have type specific value assignment like this:
ws.Cell("B2").SetValue<T>(object);
The documentation is very thorough, and you can get the package through nuget (note it requires the DocumentFormat.OpenXML package installed as well)
EDIT:
I missed the part about multiple worksheets so here it is. The XLWorkbook type has a property Worksheets, which is an enumerable of all the worksheets in the workbook. Use this to get the worksheets you want to delete rows on
My program, every hour, does some math calculations and saves these result into excel. When it first run(lets say 08:00 AM) it creates a excel workbook and one sheet namely "Sheet1". It saves excel and releases the COM objects. so far everything is fine.
My problem begins with second run (09:00 AM). when it tries to save new results, it overwrites existing excel file (This is OK, the way i want it) but it overwrites Sheet1 which was created in 08:00 AM. I want it to save new result in Sheet2.
In third run, i want it to save result in Sheet3
In fourth run, i want it to save result in Sheet4. so on so forth..
How can i change my code to do like above ?
thanks in advance..
My Code:
using excelApp = Microsoft.Office.Interop.Excel;
public static void Main(string[] arg)
{
while (true)
{
writeToExcel();
int wait = 3600 * 1000;
System.Threading.Thread.Sleep(Convert.ToInt32(wait));
}
}
public static void writeToExcel()
{
excelApp.Application excl = new Microsoft.Office.Interop.Excel.Application();
excl.Visible = true;
//MATH CALCULATIONS......
excelApp.Workbook wb = excl.Workbooks.Add(excelApp.XlWBATemplate.xlWBATWorksheet);
excelApp.Worksheet ws1 = (excelApp.Worksheet)wb.Worksheets[1];
excelApp.Worksheet ws2 = (excelApp.Worksheet)wb.Sheets.Add();
excelApp.Worksheet ws3 = (excelApp.Worksheet)wb.Sheets.Add();
excelApp.Worksheet ws4 = (excelApp.Worksheet)wb.Sheets.Add();
excelApp.Worksheet ws5 = (excelApp.Worksheet)wb.Sheets.Add();
excl.DisplayAlerts = false;
string fileName = string.Format(#"{0}\Data_" + DateTime.Now.Month + "-" DateTime.Now.Day + ".xlsx", Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory));
workSheet.SaveAs(fileName);
Console.WriteLine("Excel Saved Successfully!!");
excl.Quit();
// Release COM objects
if (excl != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(excl);
if (workSheet != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(workSheet);
excl = null;
workSheet = null;
GC.Collect();
}
You need to get the work book from the saved file. So at the beginning of your routine, you need a mechanism to determine if today's file already exists, if so, use the the following to get your Workbook.
Workbook WB = ExcelApp.Workbooks.Open(fileName, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
Hopefully this helps you see the error in your ways.
I create an Excel file from c# with data validation-it seem like combo with chosen possibility
string mList1 = "=ProductCode";
oRng = oSheet.get_Range("H8", "H9");
oRng.Name = "ProductCode";
int t = dt.Rows.Count + 2;
string st = "F" + t;
oRng = oSheet.get_Range("F2", st);
oRng.Validation.Add(XlDVType.xlValidateList,
XlDVAlertStyle.xlValidAlertStop,
Missing.Value, mList1, Missing.Value);
Now I want to read the Excel file and also the chosen item from the combo. I have successfully read all the data but the data validation.
Read the data-
Microsoft.Office.Interop.Excel.Application ExcelObj = null;
ExcelObj = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook theWorkbook = ExcelObj.Workbooks.Open("C:\\Documents and Settings\\rachelg\\My Documents\\xxx.xls"
,Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
Microsoft.Office.Interop.Excel.Sheets sheets = theWorkbook.Worksheets;
Microsoft.Office.Interop.Excel.Worksheet worksheet = (Microsoft.Office.Interop.Excel.Worksheet)sheets.get_Item(1);
for(int x = 1; x <= 5; x++)
{
string sd = ((Microsoft.Office.Interop.Excel.Range)worksheet.Cells[x, 1]).Text.ToString();
System.Console.WriteLine(sd);//this one column
}
in different column I have the data validation but I don't know to access into it.
That seems a bit much for what is largely a simple operation.
Whilst not answering your question directly, this post I made a while back might help: accessing data record from Excel in VB.NET.