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++;
Related
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];
}
}
}
}
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();
}
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";
}
}
}
}
}
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);
int rowsCount = 0;
//This checks to see that both textbox for items and subitems do not gain focus at the same time
if (textBoxSubItems.Text != string.Empty)
txtItems.Enabled = false;
else
txtItems.Enabled = true;
if (comboBoxItems.SelectedItem != null)
{
int idx = dataGridViewTimesheet.Rows.Add();
DataGridViewRow row = dataGridViewTimesheet.Rows[idx];
row.Cells["items"].Value = comboBoxItems.SelectedItem.ToString() + "-" + textBoxSubItems.Text;
row.Cells["fromTime"].Value = DateTime.Now.ToLongTimeString();
row.Cells["toTime"].Value = null;
row.Cells["duration"].Value = null;
row.Cells["subTotal"].Value = null;
// row.Cells["comments"].Value = "1";
}
else
MessageBox.Show("Please select an item");
string strGetColumnValue;
if (dataGridViewTimesheet.Rows.Count != 0)
rowsCount = dataGridViewTimesheet.Rows.Count;
else
MessageBox.Show("No row in the datagridview");
while (dataGridViewTimesheet.Rows.Count > 0)
{
try
{
if (dataGridViewTimesheet.CurrentRow != null)
for (int counter = 0; counter < dataGridViewTimesheet.Columns.Count; counter++)
{
if (dataGridViewTimesheet.Columns[counter].Index == 3)
{
strGetColumnValue = dataGridViewTimesheet.Rows[rowsCount].Cells[counter].Value.ToString();
dataGridViewTimesheet.Rows[rowsCount - 1].Cells[3].Value = strGetColumnValue;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Please i have 6 columns in a datagridview, the rows are added dynamically. What i want is when rows are more than one in the datagridview it should assign the value of the second column on the current(the last row created) row to the third column of the previous row. How do I achieve this.
Try this kind of thing
int count =1;
foreach (DataGridRow row in dataGridViewTimesheet.Rows)
{
if (count % 2 == 0)
{
string secondColumn = dataGridViewTimesheet.Rows[count -1].Cells[1].ToString();
dataGridViewTimesheet.Rows[count].Cells[2].Value = secondColumn;
}
count++;
}