OpenXML CellValue is giving Hexadecimal Value While Reading in C# - c#

foreach (Row row in sheetData.Elements<Row>())
{
ArrayList data = new ArrayList();
if (!firstRow)
{
int count = row.Elements<Cell>().Count();
//await Console.Out.WriteLineAsync($"Cell Count: [{count}]");
var firstCol = true;
foreach (Cell cell in row.Elements<Cell>())
{
if (!firstCol)
{
int id = -1;
if (cell.DataType != null && cell.DataType == CellValues.SharedString)
{
if (int.TryParse(cell.InnerText, out id))
{
var value = workbookPart.SharedStringTablePart.SharedStringTable.Elements<SharedStringItem>().ElementAt(id).InnerText;
data.Add(value);
}
}
else if (cell.DataType == null && cell.CellValue != null)
{
data.Add(cell.CellValue.Text.ToString());
}
else
{
data.Add(string.Empty);
}
}
else
{
firstCol = false;
}
}
rowList.Add(data);
}
else
{
firstRow = false;
}
}
When CellValue is not null the value come is the shape of Hexadecimal format which is saved as string in the ArrayList. I want the Actual Value which is saved in the Excel sheet.

Instead of using cell.InnerText here
if (int.TryParse(cell.InnerText, out id))
you should use cell.CellValue.Text so change that row to
if (int.TryParse(cell.CellValue.Text, out id))
I used to do the following when reading a string text from a cell
private static string ReadCell(DocumentFormat.OpenXml.Spreadsheet.Cell cell, DocumentFormat.OpenXml.Packaging.WorkbookPart workbookPart)
{
var cellValue = cell.CellValue;
var text = (cellValue == null) ? cell.InnerText : cellValue.Text;
if ((cell.DataType != null) && (cell.DataType == CellValues.SharedString))
{
text = workbookPart.SharedStringTablePart.SharedStringTable
.Elements<DocumentFormat.OpenXml.Spreadsheet.SharedStringItem>().ElementAt(Convert.ToInt32(cell.CellValue.Text)).InnerText;
}
return (text ?? string.Empty).Trim();
}

Related

C# Winforms Validating DataGridView Cells

I have a DataGridView on a WinForms that the user pastes the data or imports from an Excel file. I'm validating the cells through a button click, which calls the method below. I'm wondering if there is a "better" way of writing the if/else statements.
Note: I am aware of the DataGridView.CellValidating event but I would rather not use that, unless someone suggests a better work around using this event than the method I have written.
Here is my method being called from the button_Click
private async void ValidateGridCells()
{
string numberHeader = dgvAddNumbers.Columns[0].HeaderText;
string accountHeader = dgvAddNumbers.Columns[1].HeaderText;
string statusHeader = dgvAddNumbers.Columns[2].HeaderText;
string dateHeader = dgvAddNumbers.Columns[3].HeaderText;
string acLengthHeader = dgvAddNumbers.Columns[4].HeaderText;
if (dgvAddNumbers.Rows.Count > 1)
{
foreach (DataGridViewRow row in dgvAddNumbers.Rows)
{
foreach (DataGridViewCell cell in row.Cells)
{
cell.ErrorText = "";
}
}
}
if (numberHeader.Equals("Number"))
{
//Number cannot exist in DB already from Numbers table
//Number can only appear once in datagridview
//Number must be 10 digits ONLY
var numbersList = new List<string>();
var aniListFromDb = new List<dynamic>(await DataAccess.FetchNumbersFromDatabase(ConfigReader.GetValue("ConnectionStrings", "DB"), ConfigReader.GetValue("StoredProcedures", "FetchAllNumbers")));
foreach (DataGridViewRow row in dgvAddNumbers.Rows)
{
numbersList.Add(row.Cells[0].ToString());
}
foreach (DataGridViewRow row in dgvAddNumbers.Rows)
{
if (row.IsNewRow) { continue; }
//Check for empty Number and length
if (string.IsNullOrEmpty(row.Cells[0].Value.ToString()) || row.Cells[0].Value.ToString().Length != 10)
{
row.Cells[0].ErrorText = "Number must be 10 digits";
continue;
}
//Check for multiples of the same Number
else if (numbersList.Where(a => a == row.Cells[0].Value.ToString()).Count() > 0)
{
row.Cells[0].ErrorText = "Number already exists in the grid";
continue;
}
//Check if Number is already in DB
else if (aniListFromDb.Where(a => a.ANI == row.Cells[0]).Count() > 0)
{
row.Cells[0].ErrorText = "Number already exists in the database";
continue;
}
//Check for valid NPA/NXX
else
{
var npa = row.Cells[0].Value.ToString()[..3];
var nxx = row.Cells[0].Value.ToString().Substring(3, 3);
//Validate NPA
if (_npaList.Where(x => x.NPA == npa).Count() == 0)
{
row.Cells[0].ErrorText = "NPA not valid";
}
//Validate NXX
if (_nxxList.Where(x => x.NXX == nxx).Count() == 0)
{
row.Cells[0].ErrorText = "NXX not valid";
}
}
}
}
if (accountHeader.Equals("Account Number"))
{
//Account Number must exist
foreach (DataGridViewRow row in dgvAddNumbers.Rows)
{
if (row.IsNewRow) { continue; }
if (string.IsNullOrEmpty(row.Cells[1].Value.ToString()))
{
row.Cells[1].ErrorText = "Account Number is missing";
continue;
}
else if (MainForm.activeAccounts.Where(a => a.AccountNumber == row.Cells[1].Value.ToString()).Count() == 0)
{
row.Cells[1].ErrorText = "Account Number is not valid";
}
}
}
if (statusHeader.Equals("Status"))
{
//Status needs to be the same as in enums
foreach (DataGridViewRow row in dgvAddNumbers.Rows)
{
if (row.IsNewRow) { continue; }
if (string.IsNullOrEmpty(row.Cells[2].Value.ToString()))
{
row.Cells[2].ErrorText = "Status is missing";
continue;
}
else if (!Enum.GetNames(typeof(FormConstants.StatusDropDownValues)).Any(e => row.Cells[2].Value.ToString().Contains(e)))
{
row.Cells[2].ErrorText = "Status is not valid";
}
}
}
if (dateHeader.Equals("Active Date"))
{
//Activate Date must be current or greater date
DateTime today = DateTime.Now;
foreach (DataGridViewRow row in dgvAddNumbers.Rows)
{
DateTime dt = DateTime.Now.AddDays(-1);
if (row.IsNewRow) { continue; }
if (!string.IsNullOrEmpty(row.Cells[3].Value.ToString()))
{
dt = DateTime.Parse(row.Cells[3].Value.ToString());
}
if (string.IsNullOrEmpty(row.Cells[3].Value.ToString()))
{
row.Cells[3].ErrorText = "Activation Date is missing";
continue;
}
else if (dt < today)
{
row.Cells[3].ErrorText = "Date must be today or in the future";
}
}
}
if (acLengthHeader.Equals("AC Length"))
{
//Account code length needs to be >=0 and <=10
foreach (DataGridViewRow row in dgvAddNumbers.Rows)
{
if (row.IsNewRow) { continue; }
if (string.IsNullOrEmpty(row.Cells[4].Value.ToString()))
{
row.Cells[4].ErrorText = "Account length is missing";
continue;
}
else if ((int.Parse(row.Cells[4].Value.ToString()) < 0) || (int.Parse(row.Cells[4].Value.ToString()) > 10))
{
row.Cells[4].ErrorText = "Length can only be between 0-10";
}
}
}
}
This is how I am pasting the data to the DataGridView from the KeUp event on the grid:
private void PasteToDataGridView(bool clearRows)
{
var gridView = this.dgvAddNumbers;
DataObject o = (DataObject)Clipboard.GetDataObject();
if (o.GetDataPresent(DataFormats.Text))
{
if (clearRows)
{
if (gridView.RowCount > 0)
gridView.Rows.Clear();
}
string[] pastedRows = Regex.Split(o.GetData(DataFormats.Text).ToString().TrimEnd("\r\n".ToCharArray()), "\r\n");
foreach (string pastedRow in pastedRows)
{
string[] pastedRowCells = pastedRow.Split(new char[] { '\t' });
int myRowIndex = gridView.Rows.Add();
using (DataGridViewRow myDataGridViewRow = gridView.Rows[myRowIndex])
{
for (int i = 0; i < pastedRowCells.Length; i++)
myDataGridViewRow.Cells[i].Value = pastedRowCells[i];
}
}
}
}

Formating cells using NPOI

Does someone know why it is inserting text instead of numbers? I defined it before inserting the value, I defined it when inserting the value. And nothing.
foreach (var result in Query.listResults)
{
var row = sheet.CreateRow(rowIndex);
var rowFiltr = result;
int split = rowFiltr.Split(';').Count(), i = 0;
foreach (var rowResult in rowFiltr.Split(';'))
{
if (i == 3 || i == 4)
{
row.CreateCell(i, CellType.Numeric).SetCellValue(rowResult);
}
else
{
row.CreateCell(i).SetCellValue(rowResult);
}
i++;
}
rowIndex++;
}
workbook.Write(fs);
Creating format style didn't help. Now the file is not creating.
if (i == 3 || i == 4)
{
var cell = row.CreateCell(i);
ICellStyle cellStyle = workbook.CreateCellStyle();
cellStyle.DataFormat = (i == 3) ? HSSFDataFormat.GetBuiltinFormat("0") : HSSFDataFormat.GetBuiltinFormat("0.00");
cell.SetCellType(CellType.Numeric);
cell.SetCellValue(rowResult);
}
else
{
row.CreateCell(i).SetCellValue(rowResult);
}
i++;

values are not transforming in datagridview as per condition c#

I want to change the value of datagridview column according to the condition if the cell.value is minimum of the all values the cell must contain "L" , that is low. If the value is highest of all the cell.value = "H" rest should be medium. This is what i have done so far:
//Function to display interaction as High, Medium and Low
public void ShowInteraction(DataGridView dv, string columnName)
{
//MAX
var Max = dv.Rows.Cast<DataGridViewRow>()
.Max(r => Convert.ToDouble(r.Cells[columnName].Value));
//MIN
List<double> arr = new List<double>();
foreach (DataGridViewRow row in dv.Rows)
{
if (row != null && row.Cells[columnName].Value != null)
{
arr.Add(Convert.ToDouble(row.Cells[columnName].Value.ToString()));
}
}
double Min = arr.Min();
//show interaction in datagridview as H, M nad L
foreach (DataGridViewRow row in dv.Rows)
{
if (row.Cells[columnName].Value != null)
{
if ((double)row.Cells[columnName].Value == Min)
{
row.Cells[columnName].Value = Convert.ToString( "L" );
}
else if ((double)row.Cells[columnName].Value == Max)
{
row.Cells[columnName].Value = Convert.ToString( "H" );
}
else if ((double)row.Cells[columnName].Value < Max && (double)row.Cells[columnName].Value > Min)
{
row.Cells[columnName].Value = Convert.ToString("M");
}
else if ((double)row.Cells[columnName].Value == 0.0000)
{
row.Cells[columnName].Value = Convert.ToString("N");
}
else
{
row.Cells[columnName].Value = Convert.ToString("L");
}
}
}
}
And this the result:
My problem is that the medium and high values are showing but some times it escape the lowest value and do not show any "L" at some columns. The data generated is random data.
I cleaned up your code a little bit and I'm not seeing the error you are. Let me know if this helps you with your issue or not.
public partial class MainForm : Form {
public MainForm() {
InitializeComponent();
DataGridView dgv = new DataGridView();
dgv.Columns.Add("columnName", "columnName");
dgv.AllowUserToAddRows = false;
dgv.Rows.Add(21.0);
dgv.Rows.Add(15.0);
dgv.Rows.Add(20.0);
dgv.Rows.Add(25.0);
dgv.Rows.Add(30.12354122);
this.Controls.Add(dgv);
ShowInteraction(dgv, "columnName");
}
public void ShowInteraction(DataGridView dv, string columnName) {
List<double> values = dv.Rows.Cast<DataGridViewRow>().Select(row => Convert.ToDouble(row.Cells[columnName].Value)).ToList();
double Max = values.Max();
double Min = values.Min();
foreach (DataGridViewRow row in dv.Rows)
{
if (row.Cells[columnName].Value != null)
{
double cellValue = Convert.ToDouble(row.Cells[columnName].Value);
var cell = row.Cells[columnName];
if (cellValue == Min)
{
cell.Value = "L";
}
else if (cellValue == Max)
{
cell.Value = "H";
}
else if (cellValue < Max && cellValue > Min)
{
cell.Value = "M";
}
else if (cellValue == 0)
{
cell.Value = "N";
}
else
{
cell.Value = "L";
}
}
}
}
}

How to read blank cell column value of excel using OpenXML in C#

Here in my Execl Sheet there are some blank value column cell, so When I use This code then in get Error "Object reference not set to an instance of an object".
foreach (Row row in rows)
{
DataRow dataRow = dataTable.NewRow();
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
dataRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
}
dataTable.Rows.Add(dataRow);
}
private static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
string value = cell.CellValue.InnerXml;
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
}
else
{
return value;
}
}
The "CellValue" don't necessarily exist. In your case it is "null", so you have your error.
To read your blank cell :
If you don't want format the result depending what the cell contain, try
private static string GetCellValue(Cell cell)
{
return cell.InnerText;
}
if you want to format your cell before returning the value of it
private static string GetCellValue(SpreadsheetDocument doc, Cell cell)
{
// if no dataType, return the value of the innerText of the cell
if (cell.DataType == null) return cell.InnerText;
// depending type of the cell
switch (cell.DataType.Value)
{
// string => search for CellValue
case CellValues.String:
return cell.CellValue != null ? cell.CellValue.Text : string.Empty;
// inlineString => search of InlineString
case CellValues.InlineString:
return cell.InlineString != null ? cell.InlineString.Text.Text : string.Empty;
// sharedString => search for the SharedString
case CellValues.SharedString:
// is sharedPart exist ?
if (doc.WorkbookPart.SharedStringTablePart == null) doc.WorkbookPart.SharedStringTablePart = new SharedStringTablePart();
// is the text exist ?
foreach (SharedStringItem item in doc.WorkbookPart.SharedStringTablePart.SharedStringTable.Elements<SharedStringItem>())
{
// the text exist, return it from SharedStringTable
if (item.InnerText == cell.InnerText) return cell.InnerText;
}
// no text in sharedStringTable, create it and return it
doc.WorkbookPart.SharedStringTablePart.SharedStringTable.Append(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(cell.InnerText)));
doc.WorkbookPart.SharedStringTablePart.SharedStringTable.Save();
return cell.InnerText;
// default case : bool / number / date
// return the value of the cell in plain text
// you can parse types depending your needs
default:
return cell.InnerText;
}
}
Two usefull documentations:
About cell :
https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.cell(v=office.14).aspx
About SharedString :
https://msdn.microsoft.com/en-us/library/office/gg278314.aspx

Handle merged cells in Epplus Excel conversion to HTML

I'm using Epplus to render an Excel spreadsheet into HTML. So far it's going very, very well, except for one thing... spanning merged cells. I just can't seem to get the logic right. I thought I would throw it out there to see how the community would deal with it. Here is my code so far.
public String ParseExcelStamps(String FileName)
{
FileInfo theFile = new FileInfo(FileName);
String html = "";
using (ExcelPackage xlPackage = new ExcelPackage(theFile))
{
var workbook = xlPackage.Workbook;
if (workbook != null)
{
for (int j = 1; j <= workbook.Worksheets.Count; j++)
{
Tab tab = new Tab();
html+= "<table style='border-collapse: collapse;font-family:arial;'><tbody>";
var worksheet = workbook.Worksheets[j];
tab.Title = worksheet.Name;
if (worksheet.Dimension == null) { continue; }
int rowCount = 0;
int maxColumnNumber = worksheet.Dimension.End.Column;
var convertedRecords = new List<List<string>>(worksheet.Dimension.End.Row);
var excelRows = worksheet.Cells.GroupBy(c => c.Start.Row).ToList();
excelRows.ForEach(r =>
{
rowCount++;
html += String.Format("<tr>");
var currentRecord = new List<string>(maxColumnNumber);
var cells = r.OrderBy(cell => cell.Start.Column).ToList();
Double rowHeight = worksheet.Row(rowCount).Height;
for (int i = 1; i <= maxColumnNumber; i++)
{
var currentCell = cells.Where(c => c.Start.Column == i).FirstOrDefault();
//look aheads for colspan and rowspan
ExcelRangeBase previousCellAbove = null;
ExcelRangeBase previousCell = null;
ExcelRangeBase nextCell = null;
ExcelRangeBase nextCellBelow = null;
try { previousCellAbove = worksheet.Cells[rowCount-1, i]; }catch (Exception) { }
try { previousCell = worksheet.Cells[rowCount, (i - 1)]; }catch (Exception) { }
try { nextCell = worksheet.Cells[rowCount, (i + 1)]; }catch (Exception) { }
try { nextCellBelow = worksheet.Cells[rowCount+1, i]; }catch (Exception) { }
if ((previousCell != null) && (previousCell.Merge) && (currentCell != null) && (currentCell.Merge)){continue;}
if ((previousCellAbove != null) && (previousCellAbove.Merge) && (currentCell != null)) {continue; }
if (currentCell == null)
{
html += String.Format("<td>{0}</td>", String.Empty);
}
else
{
int colSpan = 1;
int rowSpan = 1;
if ((nextCell != null) && (nextCell.Merge) && (currentCell.Merge)) {
colSpan = 2;
// Console.WriteLine(String.Format("{0} - {1}", currentCell.Address, nextCell.Address));
}
if ((nextCellBelow != null) && (nextCellBelow.Merge) && (currentCell.Merge)) {
Console.WriteLine(String.Format("{0} - {1}", currentCell.Address, nextCellBelow.Address));
}
html += String.Format("<td colspan={0} rowspan={1}>{2}</td>", colSpan, rowSpan, currentCell.Value);
}
}
html += String.Format("</tr>");
});
html += "</tbody></table>";
}//worksheet loop
}
}
return html;
}
As far as I can tell this is exactly what you need. What you were missing was the MergedCells property on the worksheet which lists all merged cells in the sheet.
My code handles row spans, column spans, and both at the same time. I did some testing with a spreadsheet that included both row, column and row/column spanning. In all cases they worked perfectly.
Code
int colSpan = 1;
int rowSpan = 1;
//check if this is the start of a merged cell
ExcelAddress cellAddress = new ExcelAddress(currentCell.Address);
var mCellsResult = (from c in worksheet.MergedCells
let addr = new ExcelAddress(c)
where cellAddress.Start.Row >= addr.Start.Row &&
cellAddress.End.Row <= addr.End.Row &&
cellAddress.Start.Column >= addr.Start.Column &&
cellAddress.End.Column <= addr.End.Column
select addr);
if (mCellsResult.Count() >0)
{
var mCells = mCellsResult.First();
//if the cell and the merged cell do not share a common start address then skip this cell as it's already been covered by a previous item
if (mCells.Start.Address != cellAddress.Start.Address)
continue;
if(mCells.Start.Column != mCells.End.Column) {
colSpan += mCells.End.Column - mCells.Start.Column;
}
if (mCells.Start.Row != mCells.End.Row)
{
rowSpan += mCells.End.Row - mCells.Start.Row;
}
}
//load up data
html += String.Format("<td colspan={0} rowspan={1}>{2}</td>", colSpan, rowSpan, currentCell.Value);

Categories

Resources