I have a DataGridView where the values found in the 1st column should not be repeated, and neither should it have any blank columns. This is the method I've implemented in order to remove this repetition:
public static void eliminateRepetition(DataGridView table)
{
for (int currentRow = 0; currentRow < table.RowCount; currentRow++)
{
DataGridViewRow rowToCompare = table.Rows[currentRow];
foreach (DataGridViewRow row in table.Rows)
{
if (row.Equals(rowToCompare)) // If the row to compare is the current row, skip
{
continue;
}
if (row.Cells[0].Value != null && rowToCompare.Cells[0].Value != null)
{
if (int.Parse(row.Cells[0].Value.ToString()) != int.Parse(rowToCompare.Cells[0].Value.ToString())) // 1st column values must match in order to be considered as a repetition
{
continue;
}
else
{
table.Rows.Remove(row); // Remove repeated row
}
}
else
{
table.Rows.Remove(row); // Remove blank rows
}
}
}
}
The result I have is always 2 rows with the same value in the 1st column.
Any help will be much appreciated.
After removing a row from the table, the indexes change. Next iteration will not be as expected.
I think it would be better to create a list of objects. Make them unique in terms of your rule. Then set the DataSource property of the datagridview to the object list you have.
Related
I wanted to remove unwanted columns towards the last or any blank columns without header in between columns, then remove formatting from excel worksheet from each rows and column using EPPlus.
Please find the sample progress.
public static class EpPlusExtension
{
public static string[] GetHeaderColumns(this ExcelWorksheet sheet)
{
List<string> columnNames = new List<string>();
foreach (var firstRowCell in sheet.Cells[sheet.Dimension.Start.Row, sheet.Dimension.Start.Column, 1, sheet.Dimension.End.Column])
columnNames.Add(firstRowCell.Text);
return columnNames.ToArray();
}
public static ExcelWorksheet RemoveCellFormatter(this ExcelWorksheet worksheet)
{
try
{
dynamic cellValue = null;
int lastColumnWithHeaderIndex = worksheet.GetHeaderColumns().Count();
for (int i = worksheet.Dimension.Start.Row; i <= worksheet.Dimension.End.Row; i++)
{
for (int j = worksheet.Dimension.Start.Column; j <= lastColumnWithHeaderIndex; j++)
{
if (worksheet.Cells[i, j].Value != null)
{
cellValue = worksheet.Cells[i, j].Value;
worksheet.Cells[i, j].Clear();
worksheet.Cells[i, j].Value = cellValue;
cellValue = null;
}
}
}
return worksheet;
}
catch (Exception ex)
{
_logger.LogError($"Error Message: Exception While clearing formatting from worksheet. |Exception: {ex.Message}");
return worksheet;
}
}
}
Sample code handles the last unwanted columns:
How to handle also null columns in-between?
Thanks in advance
I am not an EPPlus expert and was unable to find a method that would remove “empty” columns. However, as VDWWD notes, it should not be difficult to loop through the rows of a column and check to see if all the cells in that column are null and if so, then remove that column.
Obviously, if there are many rows, then this may not be the best approach. If there are many rows and columns, there is possibly another option, however, in this case, I will assume the execution time is acceptable. In my tests, the worksheet contained 735 rows and 9 columns with two (2) empty columns and it took less than a second.
Given this, a simple solution is to loop through the columns. In each column iteration, loop through all the rows. We will use two variables: a bool valueFound and set it to false. This will be used to indicate if we found a non null value in a cell. And a List<int> emptyColIndexes is a list of ints to hold the indexes of the columns that are "empty" in the worksheet.
NOTE: it should be obvious that we DO NOT want to “delete” the empty
column as soon as we find one. We only want to store its index and
delete the columns later. The reason for this… is… deleting the column
while “looping” through the columns will obviously cause problems.
Therefore, we only want to get a list of the indexes of the “empty”
columns.
When looping through the rows of a column, IF we find a value that is NOT null, then we set valueFound to true and exit the rows loop using a break command. If we loop through all the rows in a column and exit the loop, we will check the valueFound variable, IF it is false, then this would mean all the row values in that column are null, then we add that columns index to the emptyColIndexes list.
Lastly, after collecting all the “empty” column indexes, we loop through the indexes and delete the columns.
NOTE: this loop needs to start from the “last” column index and move
to the first. This should be obvious being that if we delete a column
on the left of another column in the list, then the indexes will get
messed up. Basically, we want to delete the columns from the right to
left. Therefore, the loop starts at the last index and goes to the
first index.
In addition, and it is not important, however, it appears that returning the worksheet in the RemoveCellFormatter is unnecessary.
Below is the code that demonstrates what is described above.
public static void RemoveEmptyColumns(ExcelWorksheet worksheet) {
try {
List<int> emptyColIndexes = new List<int>();
bool valueFound;
for (int curCol = worksheet.Dimension.Start.Column; curCol <= worksheet.Dimension.End.Column; curCol++) {
valueFound = false;
for (int curRow = worksheet.Dimension.Start.Row; curRow <= worksheet.Dimension.End.Row; curRow++) {
if (worksheet.Cells[curRow, curCol].Value != null) {
valueFound = true;
break;
}
}
if (!valueFound) {
emptyColIndexes.Add(curCol);
}
}
if (emptyColIndexes.Count > 0) {
for (int i = emptyColIndexes.Count - 1; i >= 0; i--) {
worksheet.DeleteColumn(emptyColIndexes[i]);
}
}
}
catch (Exception ex) {
MessageBox.Show("Error Message: Exception while removing empty columns from worksheet. " + ex.Message);
}
}
I am binding data from excel file in a list on button click and this works perfectly. Finally the data is binded to a DataGridView. Now I want to iterate the list to check if there are any data that isn't included to the database after binding to a DataGridView. If any data mismatches, then it should highlight the specific row with red color in the DataGridView. Note: There could be multiple data that will not match. Something as the below image and the code tried:
grdUpload.Rows.Clear();
for (int i = 0; i < lstData.Count; i++) //lstData - The Data List
{
if (Facede.ExcelUpload.CheckIfExists(lstData)) //Checking if any data mismatches
{
grdUpload.DataSource = lstData;
grdUpload.Rows[i].DefaultCellStyle.BackColor = Color.Red; //Highlight the row data that mismatches
}
else
{
grdUpload.DataSource = lstData;
}
}
public bool CheckIfExists(List<Data> lst)
{
bool flag = false;
foreach (Data d in lst)
{
string Query = "SELECT M.EmpNo FROM Data m WHERE M.EmpNo = '" + d.EmpNo + "'";
DataTable dt = SelectData(Query);
if (dt != null && dt.Rows.Count > 0)
{
flag = true;
}
else
{
flag = false;
}
}
return flag;
}
Now the issue is it doesn't highlight the specific row if data like EmpNo mismatches. Anything that I am missing here?
Problem is in your for loop.
You are firstly binding data to your datagridview.
Then you are entering for loop
Inside it you ask if condition is met and if it is you AGAIN bind same data to datagridview but after it you color it.
For loop continues and it again enters part where it meets condition and AGAIN you BIND same data but now you overwrite colored data with new (but same) data and then color some new row.
So what you need to do is
Load data into datagridview
Loop through datagridviewrows and if meet condition color that row
So code should look like this:
//Here you bind your data to datagridview
//In code bellow if you want to get row's column's data use
//row.Cells["CELL_VALUE"].Value (convert to what datatype you need before comparing)
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (condition)))
{
dataGridView1.Rows[row.Index].DefaultCellStyle.BackColor = Color.Red;
}
}
I am trying to make some cells readonly on specific condition(flag). But i'm having problem to set the exact condition. I have a not null bit column and trying to set condition on its value.
Here is my code :
private void grdOtherItemsInfo_InitializeLayout(object sender, InitializeLayoutEventArgs e)
{
UltraGridBand band;
try
{
band = e.Layout.Bands[0];
band.ColHeaderLines = 2;
foreach (UltraGridRow row in **can't find right option**)
{
if (row.Cells[OtherItemStoreRequisitionForBatchChild.IsAutoDispense].Value.ToString() == "1")
{
band.Columns[OtherItemStoreRequisitionForBatchChild.IsAutoDispense].CellActivation = Activation.ActivateOnly;
band.Columns[OtherItemStoreRequisitionForBatchChild.IndentedUOM].CellActivation = Activation.ActivateOnly;
band.Columns[OtherItemStoreRequisitionForBatchChild.IndentedQty].CellActivation = Activation.ActivateOnly;
}
}
}
foreach (UltraGridRow row in grdOtherItemsInfo.Rows)
{
foreach (UltraGridRow urow in grdChemicalItemInfo.Rows[row.Index].ChildBands[0].Rows)
{
if (Convert.ToBoolean(urow.Cells[OtherItemStoreRequisitionForBatchChild.IsAutoDispense].Value))
{
foreach (UltraGridCell col in urow.Cells)
{
col.Activation = Activation.ActivateOnly;
}
}
}
}
The band has no rows property. If you need to iterate through all the rows you need to call grdOtherItemsInfo.Rows. You may use code like this:
private void grdOtherItemsInfo_InitializeLayout(object sender, Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs e)
{
UltraGridBand band;
try
{
band = e.Layout.Bands[0];
band.ColHeaderLines = 2;
// Rows collection of the grid contains the rows in Band[0] or the top level of GroupByRows
foreach (UltraGridRow row in this.grdOtherItemsInfo.Rows)
{
// Check if the row is DataRow, otherwise you will get an exception when you call Cell property of not data row
if (row.IsDataRow)
{
// Cashing the cell so not taking it twice
UltraGridCell cell = row.Cells[OtherItemStoreRequisitionForBatchChild.IsAutoDispense];
if (cell.Value.ToString() == "1")
{
// Setting the cells' Activation will set each cell its own activation
// If you set it to the column all the cells in the column will have same activation
cell.Activation = Activation.ActivateOnly;
row.Cells[OtherItemStoreRequisitionForBatchChild.IndentedUOM].Activation = Activation.ActivateOnly;
row.Cells[OtherItemStoreRequisitionForBatchChild.IndentedQty].Activation = Activation.ActivateOnly;
}
}
}
}
catch(Exception ex)
{
// TODO: ...
}
}
Note that in your code you are going through all the rows and you are setting the column CellActivation depending on some row's cell value. In the result of this the CellActivation may change several times but in the end it will depend on the value of the cell in the last row. Setting the CellActivation on the column force all the cells in that column to have same CellActivatio. If you need different CellActivation for each cell in a column you need to set the Activation property of each individual cell - this is what the code I send you do.
Check also this link showing how to iterate grid's rows
I am trying to remove a all marked/checked rows in an unbound datagridview. The checkbox column was added programatically. I know that I have to use
RequestedEmpGrid.Rows.Remove(RequestedEmpGrid.Rows[ForRequestRow.Index]);
to remove the row from the grid. However, I am having trouble getting the row index of the multiple checked rows in the datagridview. My looping is not working. Help?
//REMOVE ALL CHECKED
foreach (DataGridViewRow ForRequestRow in RequestedEmpGrid.Rows)
{
if (Convert.ToBoolean(ForRequestRow.Cells[MarkColumn.Name].Value) == true)
{
RequestedEmpGrid.Rows[ForRequestRow.Index].Selected = true;
RequestedEmpGrid.Rows.Remove(RequestedEmpGrid.Rows[ForRequestRow.Index]);
}
}
Why dont you try
RequestedEmpGrid.Rows.Remove(ForRequestRow)
It will just remove the row from gridview but not from Data source, so do not rebind the gridview as it will add the deleted rows again.
And if you want to delete from datasource as well just delete the selected rows from DB and then rebind the gridview.
You can try this code:
foreach (DataGridViewRow requestRow in RequestedEmpGrid.Rows)
{
if (Convert.ToBoolean(requestRow.Cells[MarkColumn.Name].Value))
{
RequestedEmpGrid.Rows.Remove(requestRow);
}
}
or this:
foreach (DataGridViewRow requestRow in RequestedEmpGrid.Rows)
{
if (Convert.ToBoolean(requestRow.Cells[MarkColumn.Name].Value))
{
RequestedEmpGrid.Rows.RemoveAt(requestRow.Index);
}
}
update:
How about that?
for (int i = RequestedEmpGrid.Rows.Count; i >= 0; i--)
{
var row = RequestedEmpGrid.Rows[i];
if (Convert.ToBoolean(row.Cells[MarkColumn.Name].Value))
{
RequestedEmpGrid.Rows.RemoveAt(i);
}
}
I have a DataSet with several DataTables inside. I'm displaying the DataTables in a ListView (I didn't know about databinding when I wrote the code). Anyway, I would like to remove rows from the DataTables inside the DataSet.
I have tried this:
foreach (DataRow row in dsData.Tables["Table1"].Rows)
{
//find the row that contains the username I'm after
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}
I've also tried
dsData.Tables["Table1".Rows.Delete(row);
The problem I'm experiencing is that the when you remove a row I get the exception:
Collection was modified; enumeration operation might not execute.
From what I understand it's because when you remove a row from a ListView the row below it moves up and causes all this trouble. The code itself does what it's supposed to but it's not nice to see that exception when when you run it.
I was about to rewrite the whole class with a DataGridView but would rather correct that single line if possible :).
EDIT: I'm not even sure a DataGridView would solve the problem anyway.
change the loop to a for loop counting backwards so you don't get that message.
for(int i = dsData.Tables["TAble1"].Rows; i > 0; i--)
{
if(item.SubItems[2].Text == dsData.Tables["Table1"].Rows[i - 1]["LoginName"].ToString())
dsData.Tables["Table1"].Rows.Remove(i - 1)
}
Try:
DataSet dsData = new DataSet();
List<DataRow> rowsToDelete = new List<DataRow>();
foreach (DataRow row in dsData.Tables["Table1"].Rows)
{
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
rowsToDelete.Add(row);
}
}
foreach(DataRow row in rowsToDelete)
{
dsData.Tables["Table1"].Rows.Remove(row);
}
You need a backwards for loop if you're going to be removing things
(explanation of why here)
for (int i = dsData.Tables["Table1"].Rows.Count - 1; i >= 0; i--)
{
DataRow row = dsData.Tables["Table1"].Rows[i];
//find the row that contains the username I'm after
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}
in general you can't remove items from a collection within a loop that is iterating on it
what you could do is keep a list of all the row you want to remove (creating it within the loop) and remove all of them OUTSIDE the loop
You can't modify a collection that you are iterating though with a foreach loop, from inside the loop. Do this instead:
for (int i = 0; i < dsData.Tables["Table1"].Rows.Count; i++)
{
DataRow row = dsData.Tables["Table1"].Rows[i];
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}