I try to read excel file using NPOI library.
Here is the code:
public void ReadDataFromXL()
{
try
{
for (int i = 1; i <= sheet.LastRowNum; i++)
{
IRow row = sheet.GetRow(i);
for (int j = 0; j < row.Cells.Count(); j++)
{
var columnIndex = row.GetCell(j).ColumnIndex;
var cell = row.GetCell(j);
if (cell != null)
{
switch (cell.CellType)
{
case CellType.Numeric:
var val = cell.NumericCellValue; ;
break;
case CellType.String:
var str = cell.StringCellValue;
break;
}
}
}
}
}
catch (Exception)
{
throw;
}
}
Here the content of .xlsx file that I try to read:
As you can see column X and column Y are numeric columns.
But when I start to read this columns using the code above some of the numeric values in X and Y column have been recognizes by code as string values.
For example in picture above the cell B4 is numeric type but, on cell.CellType it shows String and the value of the string is 31.724732480727\n. '\n' is appended to the value.
Any idea why some numeric values appeared as string and why '\n' appended to the value?
It looks like the datatype of the column is of String, so if you wanted to check for the double datatype (assuming its going to be in the num+'\n' format, you could try the following snippet of code.
String number = "1512421.512\n";
double res;
if (double.TryParse(number.Substring(0, number.Length - 1), out res))
{
Console.WriteLine("It's a number! " + res);
}
Related
I had no luck deleting rows in excel so now I try to clear their content and still get the error:
"Can't delete/overwrite merged cells. A range is partly merged with the another merged range. A57788:J57788".
Columns 1-10 are really merged, but how do I unmerge them?
Here's my code:
cntr = 0;
while (ws.Cells[RowNum + cntr, 1].Value == null || !ws.Cells[RowNum + cntr, 1].Value.ToString().StartsWith("Report generation date"))
{
ws.Cells[RowNum + cntr, 1, RowNum + cntr, 18].Value = "";
ws.Cells[RowNum + cntr, 1, RowNum + cntr, 10].Merge = false;
for (int i = 1; i < 17; i++)
{
ws.Cells[RowNum + cntr, i].Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.None);
ws.Cells[RowNum + cntr, i].Clear();
}
cntr++;
}
//ws.DeleteRow(RowNum, cntr);
The thing is you can not unmerge a single cell in a range, you have to unmerge the whole range.
To do that you can get the merged range that a cell belongs to using my solution here:
public string GetMergedRange(ExcelWorksheet worksheet, string cellAddress)
{
ExcelWorksheet.MergeCellsCollection mergedCells = worksheet.MergedCells;
foreach (var merged in mergedCells)
{
ExcelRange range = worksheet.Cells[merged];
ExcelCellAddress cell = new ExcelCellAddress(cellAddress);
if (range.Start.Row<=cell.Row && range.Start.Column <= cell.Column)
{
if (range.End.Row >= cell.Row && range.End.Column >= cell.Column)
{
return merged.ToString();
}
}
}
return "";
}
The second step is unmerging the whole range using:
public void DeleteCell(ExcelWorksheet worksheet, string cellAddress)
{
if (worksheet.Cells[cellAddress].Merge == true)
{
string range = GetMergedRange(worksheet, cellAddress); //get range of merged cells
worksheet.Cells[range].Merge = false; //unmerge range
worksheet.Cells[cellAddress].Clear(); //clear value
}
}
This way will cost you to lose merging of the other cells, and their value, to overcome this you can save value before clearing and unmerging then you can write it back, something like:
public void DeleteCell(ExcelWorksheet worksheet, string cellAddress)
{
if (worksheet.Cells[cellAddress].Merge == true)
{
var value = worksheet.Cells[cellAddress].Value;
string range = GetMergedRange(worksheet, cellAddress); //get range of merged cells
worksheet.Cells[range].Merge = false; //unmerge range
worksheet.Cells[cellAddress].Clear(); //clear value
//merge the cells you want again.
//fill the value in cells again
}
}
I am trying to compute the sum for same IDs in each column in the data table. In the datatable there are empty elements. When I run the following code to the line calculating colP_sum, it gives me the error if "specific cast is not valid". It seems caused by the empty elements in the data table? How should I solve it? I am sure if the datatable is filled up with numbers this code works.
for (int i = 0; i < LoadIDcount; i++)
{
string IDnow = LoadID[i, 0];
string IDsaved = LoadP_dt.Rows[i][0].ToString();
if (LoadP_dt.Rows[i][0].ToString() == IDnow)
{
for (int j = 0; j < 8760; j++)
{
string colPnow = SP_dt.Columns[j * 2 + 4].ColumnName.ToString();
double ColP_sum = (double)SP_dt.Compute(String.Format("Sum([{0}])", colPnow), String.Format("Load_ID = '{0}'", IDnow));
string colQnow = SP_dt.Columns[j * 2 + 5].ColumnName.ToString();
double ColQ_sum = (double)SP_dt.Compute(String.Format("Sum([{0}])", colQnow), String.Format("Load_ID = '{0}'", IDnow));
LoadP_dt.Rows[i][j + 2] = ColP_sum;
LoadQ_dt.Rows[i][j + 2] = ColQ_sum;
Console.WriteLine("{0} {1}", i, j);
}
}
else
{
Console.WriteLine("ID does not match");
}
}
CSVfilewriter(CSVPpath, LoadP_dt);//save the Load_P datatable to CSV file
CSVfilewriter(CSVQpath, LoadQ_dt);//save the Load_Q datatable to CSV file
//CSVfilewriter(CSVSPpath, SP_dt);//save the service point datatable to CSV file
}
if "colPnow" is not a number, that could explain it: the "Compute" and "Sum" both appear to be expecting number value
I write some data into csv file from List but some list indexes has empty string but another indexes has value
in these cases the data compared with another list wrote in the same csv file
this is my csv file opened using excel sheet
in the third column there exist ID for the the second column cell so in the coming rows i want to detect the name of the ID based on previous rows
like in row 3 it's ID is 19 and name is I/O so in the 7th row the ID is 19 and want to fill the second cell now
info : the IDs is already known above and any next ID will be exist before
by the follwing code.
bool isInList = ms.IndexOf(ShapeMaster) != -1;
if (isInList)
{
savinglabelnamefortextbox = t.InnerText;
string replacement =
Regex.Replace(savinglabelnamefortextbox, #"\t|\n|,|\r", "");
xl.Add("");
dl.Add(replacement);
ms.Add(ShapeMaster);
}
and I use the following code to write to the csv file
using (StreamWriter sw = File.CreateText(csvfilename))
{
for (int i = 0; i < dl.Count; i++)
{
var line = String.Format("{0},{1},{2}", dl[i], xl[i],ms[i]);
sw.WriteLine(line);
}
}
Try this
for (int x = 0; x < ms.Count; x++)
{
if (xl[x] != "")
{
continue;
}
else if (xl[x] == "")
{
for (int y = 0; y<xl.Count; y++)
{
if (ms[y] == ms[x])
{
xl[x] = xl[y];
break;
}
}
continue;
}
}
UPDATE: So this code is collection a SQL Query into a DataSet prior to this method. This data set is then dropped into excel in the corresponding tab at a specific cell address(which is loaded from the form) but the code below is the exporting to excel method. I am getting the following error:
An unhandled exception of type 'System.AccessViolationException' occurred in SQUiRE (Sql QUery REtriever) v1.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I have been tracking this for a while and thought I fixed it, but my solution was a false positive. So I am using a try...catch block that is breaking but not returning anything. Let me know if you all see anything that I am missing. I usually break on this line (templateSheet = templateBook.Sheets[tabName];) and on the same tabName. The tab is not locked or restricted so It can be written to and works more than half of the time.
public void ExportToExcel(DataSet dataSet, Excel.Workbook templateBook, int i, int h, Excel.Application excelApp) //string filePath,
{
try
{
lock (this.GetType())
{
Excel.Worksheet templateSheet;
//check to see if the template is already open, if its not then open it,
//if it is then bind it to work with it
//if (!fileOpenTest)
//{ templateBook = excelApp.Workbooks.Open(filePath); }
//else
//{ templateBook = (Excel.Workbook)System.Runtime.InteropServices.Marshal.BindToMoniker(filePath); }
//Grabs the name of the tab to dump the data into from the "Query Dumps" Tab
string tabName = lstQueryDumpSheet.Items[i].ToString();
templateSheet = templateBook.Sheets[tabName];
// Copy DataTable
foreach (System.Data.DataTable dt in dataSet.Tables)
{
// Copy the DataTable to an object array
object[,] rawData = new object[dt.Rows.Count + 1, dt.Columns.Count];
// Copy the values to the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
for (int row = 0; row < dt.Rows.Count; row++)
{ rawData[row, col] = dt.Rows[row].ItemArray[col]; }
}
// Calculate the final column letter
string finalColLetter = string.Empty;
string colCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int colCharsetLen = 26;
if (dt.Columns.Count > colCharsetLen)
{ finalColLetter = colCharset.Substring((dt.Columns.Count - 1) / colCharsetLen - 1, 1); }
finalColLetter += colCharset.Substring((dt.Columns.Count - 1) % colCharsetLen, 1);
/*Grabs the full cell address from the "Query Dump" sheet, splits on the '=' and
*pulls out only the cell address (i.e., "address=a3" becomes "a3")*/
string dumpCellString = lstQueryDumpText.Items[i].ToString();
string dumpCell = dumpCellString.Split('=').Last();
/*Refers to the range in which we are dumping the DataSet. The upper right hand cell is
*defined by 'dumpCell'and the bottom right cell is defined by the final column letter
*and the count of rows.*/
string firstRef = "";
string baseRow = "";
//Determines if the column is one letter or two and handles them accordingly
if (char.IsLetter(dumpCell, 1))
{
char[] createCellRef = dumpCell.ToCharArray();
firstRef = createCellRef[0].ToString() + createCellRef[1].ToString();
for (int z = 2; z < createCellRef.Count(); z++)
{ baseRow = baseRow + createCellRef[z].ToString(); }
}
else
{
char[] createCellRef = dumpCell.ToCharArray();
firstRef = createCellRef[0].ToString();
for (int z = 1; z < createCellRef.Count(); z++)
{ baseRow = baseRow + createCellRef[z].ToString(); }
}
int baseRowInt = Convert.ToInt32(baseRow);
int startingCol = ColumnLetterToColumnIndex(firstRef);
int endingCol = ColumnLetterToColumnIndex(finalColLetter);
int finalCol = startingCol + endingCol;
string endCol = ColumnIndexToColumnLetter(finalCol - 1);
int endRow = (baseRowInt + (dt.Rows.Count - 1));
string cellCheck = endCol + endRow;
string excelRange;
if (dumpCell.ToUpper() == cellCheck.ToUpper())
{ excelRange = string.Format(dumpCell + ":" + dumpCell); }
else
{ excelRange = string.Format(dumpCell + ":{0}{1}", endCol, endRow); }
//Dumps the cells into the range on Excel as defined above
templateSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
/*Check to see if all the SQL queries have been run from
if (i == lstSqlAddress.Items.Count - 1)
{
//Turn Auto Calc back on
excelApp.Calculation = Excel.XlCalculation.xlCalculationAutomatic;
/*Run through the value save sheet array then grab the address from the corresponding list
*place in the address array. If the address reads "whole sheet" then save the whole page,
*else set the addresses range and value save that.
for (int y = 0; y < lstSaveSheet.Items.Count; y++)
{
MessageBox.Show("Save Sheet: " + lstSaveSheet.Items[y] + "\n" + "Save Address: " + lstSaveRange.Items[y]);
}*/
//run the macro to hide the unused columns
excelApp.Run("ReportMakerExecute");
//save excel file as hospital name and move onto the next
SaveTemplateAs(templateBook, h);
}
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
I'm having problems with error: input string was not in a correct format. I'm trying to convert curency in datagrid. At point where I get error (this is where I set value to value variable) text variable haves value 22.22 so I don't know what is wrong with format.
public void valute()
{
int rowCount = dataGridView1.RowCount;
decimal value = 0;
for (int i = 0; i < rowCount; i++)
{
string text = dataGridView1.Rows[i].Cells[3].Value.ToString();
if (evro_check.Checked)
dataGridView1.Rows[i].Cells[3].Value = text + " €";
else if (dolar_check.Checked)
{
value = Decimal.Parse(text.Replace(',', '.'), CultureInfo.InvariantCulture);
dataGridView1.Rows[i].Cells[3].Value = value.ToString() + " $";
}
else
{
dataGridView1.Rows[i].Cells[3].Value = value + " £";
}
}
}
EDIT: Right now I'm just adding curency sign and later I'll also change € to $ and thats way I'm using additional variable (value) and not using text for other 2 currencys.
The best option you have is to use Tryparse over Parse
TryParse
This overload differs from the Decimal.Parse(String) method by returning a Boolean value that indicates whether the parse operation succeeded instead of returning the parsed numeric value. It eliminates the need to use exception handling to test for a FormatException in the event that s is invalid and cannot be successfully parsed.
A suggestion to improve the code
string text = dataGridView1.Rows[i].Cells[3].Value.ToString();
Decimal value=0;
if (Decimal.TryParse(text.Replace(',', '.'), out value))
{
//parse success
dataGridView1.Rows[i].Cells[3].Value = value.ToString() + " $"; // put the correct value
}
else {
//parse not success
dataGridView1.Rows[i].Cells[3].Value ="- $"; // Put something which allow you to identify the issue.
}
This will allow you to identify where you have wrongly formatted values in data grid.
Be careful with cultures.
For example in the UK this "10,000.10" is 10 thousand and 1/10 of your currency, while in Germany the format would be the other way around: "10.000,10".
What you do is replacing all "," with an ".". Unless you changed the current culture of your application to a format where this makes sense, then this will obviously end up with a FormatException.
You should set the CultureInfo to the culture which you are targeting.
https://msdn.microsoft.com/en-US/library/b28bx3bh%28v=vs.80%29.aspx
https://msdn.microsoft.com/en-us/library/bz9tc508%28v=vs.140%29.aspx
Also, it would be better to use a format provider, which will format you a correct monetary string according to the specified culture:
decimal value = 10000;
value.ToString("C", CultureInfo.InvariantCulture);
// Output : ¤10,000.00
value.ToString("C", CultureInfo.GetCultureInfo("de-DE"));
// Output : 10.000,00 €
value.ToString("C", CultureInfo.GetCultureInfo("en-US")).Dump();
// Output: $10,000.00
If you notice, the American format puts the currency symbol at the front, and the German one is at the end. You didn't account for any of these things either.
See: https://msdn.microsoft.com/en-us/library/0b22a4x2%28v=vs.110%29.aspx
try this
public void valute()
{
int rowCount = dataGridView1.RowCount;
decimal value = 0;
for (int i = 0; i < rowCount; i++)
{
string text = dataGridView1.Rows[i].Cells[3].Value.ToString();
if (evro_check.Checked)
dataGridView1.Rows[i].Cells[3].Value = text + " €";
else if (dolar_check.Checked)
{
if (text != "" || text != " ")
{
value = Decimal.Parse(text.Replace(',', '.'), CultureInfo.InvariantCulture);
dataGridView1.Rows[i].Cells[3].Value = value.ToString() + " $";
}
}
else
{
dataGridView1.Rows[i].Cells[3].Value = value + " £";
}
}
}
You can make use of Culture Info class.
public void valute()
{
int rowCount = dataGridView1.RowCount;
decimal value = 0;
for (int i = 0; i < rowCount; i++)
{
string text = dataGridView1.Rows[i].Cells[3].Value.ToString();
if (evro_check.Checked)
dataGridView1.Rows[i].Cells[3].Value = text.ToString("C", CultureInfo.GetCultureInfo("fr")); //€
else if (dolar_check.Checked)
{
value = Decimal.Parse(text.Replace(',', '.'), CultureInfo.InvariantCulture);
dataGridView1.Rows[i].Cells[3].Value = text.ToString("C", CultureInfo.GetCultureInfo("fr-CA")); //$
}
else
{
dataGridView1.Rows[i].Cells[3].Value = text.ToString("C", CultureInfo.GetCultureInfo("en-GB"));
}
}
}
Second Approach
make use of Unicode
public static char HexToChar(string hex)
{
return (char)ushort.Parse(hex, System.Globalization.NumberStyles.HexNumber);
}
\u00A3 is the Pound sign, £
\u20AC is the Euro sign, €.
\u0024 is the dollar sign, $.
How to insert a Symbol (Pound, Euro, Copyright) into a Textbox