I have a method that stores each line in a gridview into the database, then if the save is successful, removes the row; but if it isn't successful (cannot be stored in the db) it does not remove the row. Unfortunately, I can't get the row-removal to work properly.
This is my current code:
public static void SavePAC(PlantAreaCode_CreateView CView)
{
List<int> removeRows = new List<int>();
// For each cell in the DataGrid, stores the information in a string.
for (rows = 0; rows < CView.dataGridView1.Rows.Count; rows++)
{
correctSave = false;
if (CView.dataGridView1.Rows[rows].Cells[col].Value != null)
{
// Creates a model, then populates each field from the cells in the table.
PModel = new PlantAreaCode_Model();
PModel.AreaCode = Convert.ToString(CView.dataGridView1.Rows[rows].Cells[0].Value);
PModel.AreaName = Convert.ToString(CView.dataGridView1.Rows[rows].Cells[1].Value);
PModel.Comments = Convert.ToString(CView.dataGridView1.Rows[rows].Cells[2].Value);
// Passes the model into the Database.
Database_Facade.Operation_Switch(OPWRITE);
}
if (correctSave == true) // correctSave is set in the database insert method.
{
removeRows.Add(rows);
}
}
foreach (int i in removeRows)
{
CView.dataGridView1.Rows.RemoveAt(0); // Deletes all bar the last row, including any rows that cause errors
}
}
I have also tried:
foreach (int i in removeRows)
{
CView.dataGridView1.Rows.RemoveAt(i);
}
But that crashes at halfway, because the Rows index keeps changing each time a row is removed.
How can I achieve this? How can I remove a row if the save is successful, but keep it if there is an error?
May this help:
1] Make sure correctSave is being modified correctly.
2] Revert the loop flow, Looping backward allow to remove the row processed by the loop without affecting the index of the next row to process.
for (rows = CView.dgvCreate.Rows.Count - 1; rows >= 0 ; rows--)
3] Use CView.dataGridView1.Rows.RemoveAt(rows);
Try to populate collection of rows for removing with DataGridViewRow not with index. This works for me.
public void SavePAC(PlantAreaCode_CreateView CView)
{
List<DataGridViewRow> removeRows = new List<DataGridViewRow>();
foreach (DataGridViewRow row in CView.dataGridView1.Rows)
{
correctSave = false;
if (row.Cells[col].Value != null)
{
// Creates a model, then populates each field from the cells in the table.
PModel = new PlantAreaCode_Model();
PModel.AreaCode = Convert.ToString(row.Cells[0].Value);
PModel.AreaName = Convert.ToString(row.Cells[1].Value);
PModel.Comments = Convert.ToString(row.Cells[2].Value);
// Passes the model into the Database.
Database_Facade.Operation_Switch(OPWRITE);
}
if (correctSave == true) // correctSave is set in the database insert method.
{
removeRows.Add(row);
}
}
foreach (DataGridViewRow rowToRemove in removeRows)
{
CView.dataGridView1.Rows.Remove(rowToRemove);
}
}
You have to sort removeRows in descending order.
List<int> removeRowsDesc = removeRows.OrderByDescending(i => i);
Then use the foreach loop
foreach (int i in removeRowsDesc)
{
CView.dataGridView1.Rows.RemoveAt(i);
}
This way the reindexing wont affect the deletion.
Related
I have following code to remove the summary row/band value on a retrieve UI event. I'm certain this is the wrong way to do it but it works.
public UiEventResult AfterRetrieveData_111(object sender, RetrieveDataEventArgs e)
{
UltraGridBase baseGrid = _view.ViewGrids["ultraGrid1"];
UltraGridLayout gridLayout = baseGrid.DisplayLayout;
for (int i = 0; i < 2; i++)
{
gridLayout.Bands[i].Columns["columnA"].Formula = "''";
}
for (int i = 0; i < 3; i++)
{
gridLayout.Bands[i].Columns["columnB"].Formula = "''";
gridLayout.Bands[i].Columns["columnC"].Formula = "''";
}
Is there a way to program the retrieve so that it populates the summary row for column A/band[2] so that is uses the last value in each column? Without the above code it will sum rows under but would like for a way for it to use the last row value instead. Data will always be sorted DESC by date so last row will always be the value needed...
One way to achieve this is in InitializeRowEvent by setting the value of the columnA to the value of the last row in the child band like this:
// Update the rows only in the first band. You can also use e.Row.Band.Key == {YOU_BAND_KEY}
if (e.Row.Band.Index == 0)
{
// set the value of the columnA to the value of the last row in the child band
e.Row.Cells["columnA"].Value = e.Row.ChildBands.LastRow.Cells["columnA"].Value;
}
Note, this will not work if you edit the cells values. If you need to update the parent row value after cell update, again in InitializeRowEvent you can add this:
// look for row in the secon band
if (e.Row.Band.Index == 1)
{
// get the last row in the second band
if (e.Row == e.Row.ParentRow.ChildBands.LastRow)
{
// get the value of the last row in the second band and set it to the parent row
e.Row.ParentRow.Cells["columnA"].Value = e.Row.Cells["columnA"].Value;
}
}
This will loop through ChildBands and set parent row value with the last value in each ChildBand.
int rowCount = gridLayout.Rows.Count;
for (int i = 0; i < rowCount; i++)
{
foreach (UltraGridChildBand childBand in baseGrid.Rows[i].ChildBands)
{
foreach (UltraGridRow row in childBand.Rows)
{
row.Cells["columnA"].Value =row.ChildBands.LastRow.Cells["columnA"].Value;
}
}
}
It is possible to somehow delete all following rows from specific (empty) row ? I tried for cyclus
for (int rowNum = 1; rowNum <= worksheet.Dimension.End.Row; rowNum++)
{
var rowCells = from cell in worksheet.Cells
where (cell.Start.Row == rowNum)
select cell;
if (!rowCells.Any(cell => cell.Value != null))
{
worksheet.DeleteRow(rowNum);
}
}
but it takes minutes if in excel are millions of empty rows.
Epplus offer this method worksheet.DeleteRow(int rowFrom, int rows) but i do not know the count of all additional empty rows.
In following example i need to delete all rows 12+ but the problem is that i do not know the specific row, where the empty rows begin.
The alternative aproach can be finding last non empty row and delete everything with the range, which will be faster, but there is another issue with empty row inside the table.
ws.DeleteRow(lastFilledTableRow, workSheet.Dimension.End.Row - tableRowsCount,true);
In this example the problem is the red row but maybe i will tell the users that this kind of excel format is invalid and circumvent the problem.
I know that it is old but I could not find any solution so made one my by own.
It is checking the last row if it is empty and if yes it deletes it and doing this until finds non-empty row. (non-empty means here: all columns in this row have some value)
worksheet.TrimLastEmptyRows();
public static void TrimLastEmptyRows(this ExcelWorksheet worksheet)
{
while (worksheet.IsLastRowEmpty())
worksheet.DeleteRow(worksheet.Dimension.End.Row);
}
public static bool IsLastRowEmpty(this ExcelWorksheet worksheet)
{
var empties = new List<bool>();
for (int i = 1; i <= worksheet.Dimension.End.Column; i++)
{
var rowEmpty = worksheet.Cells[worksheet.Dimension.End.Row, i].Value == null ? true : false;
empties.Add(rowEmpty);
}
return empties.All(e => e);
}
Above solution is to delete last empty rows in the file. This will not work if file has empty rows in the middle of the rows list somewhere.
Below is the solution to identify the empty rows in the middle of the rows list.
I used combination of both above and mine to delete empty rows at the end of the rows list and empty rows in the middle of the rows list
private void TrimEmptyRows(ExcelWorksheet worksheet)
{
//loop all rows in a file
for (int i = worksheet.Dimension.Start.Row; i <=
worksheet.Dimension.End.Row; i++)
{
bool isRowEmpty = true;
//loop all columns in a row
for (int j = worksheet.Dimension.Start.Column; j <= worksheet.Dimension.End.Column; j++)
{
if (worksheet.Cells[i, j].Value != null)
{
isRowEmpty = false;
break;
}
}
if (isRowEmpty)
{
worksheet.DeleteRow(i);
}
}
}
I have a problem.
I have two loops (one for row, one for column) for creating data in DataTable. I want to check if cell is empty for column named "Name" and if it is empty just don't add this row. And here is a question: How to cancel adding row?
Got some code:
for (int i = 0; i < data.Count(); i++)
{
cell = data.ElementAt(i);
DataRow row;
row = dataTable.NewRow();
foreach (string column in columns)
{
if (row["Name"] == "")
{
row = null;
}
else
{
row[column] = cell;
}
}
if (row != null)
{
dataTable.Rows.Add(row);
}
}
But after next loop is starting it throws NullException: Object reference not set to an instance of an object.
Generally I want to add Rows to DataTable only those where value of cell is not empty at column called "Name" (i mean where is "").
What is the best way or easiest way to do it right?
Change it as:
....
foreach (string column in columns)
{
if (row["Name"] == "")
{
row = null;
break; //--> Add this line
}
else
{
row[column] = cell;
}
}
....
I have a ListView. My main goal is to be able, to copy the ListViewItems to the Clipboard and then to Excel. But I have troubles to read the ListView Cells.
To get the Columns I use:
var columnNames = new StringBuilder();
foreach (GridViewColumn column in ((GridView)(listViewSolution.View)).Columns)
{
columnNames.Append(column.Header + "\t");
}
Now I want to add the rows, but I'm only able to access the first Cell in the first column, not the second or third..:
var stringBuilder = new StringBuilder();
for (int i = 0; i < listViewSolution.Items.Count; i++)
{
stringBuilder.Append("\n");
//foreach (GridViewColumn column in ((GridView)(listViewSolution.View)).Columns)
//{
//if (column.Header != null)
//{
ListViewItem myListBoxItem =
(ListViewItem)(listViewSolution.ItemContainerGenerator.ContainerFromIndex(0)); //= Index 0 -> First Row, First Cell; Index 1 = Second Row, First Cell; But do we get: Second Column, First Row????
stringBuilder.Append(myListBoxItem.Content.ToString() + "\t");
//}
//}
}
System.Windows.Clipboard.SetData(DataFormats.Text, columnNames.ToString() + stringBuilder.ToString());
Help would be much appreciated..
You're not changing the index in the .ContainerFromIndex call each iteration. Use your loop variable there and see what you get.
So I have this datagridview that is linked to a Binding source that is binding to an underlying data table. The problem is I need to manual add rows to the datagridview.
This cannot be done while it is bound, so I have to work with the databinding.
If I add the rows to the underlying datatable, when the datatable is saved, the rows are duplicated, probably because the binding source somehow got a hold of a copy and inserted it also.
Adding it to the binding source is what I've been trying to do but it's not quite working.
Let me explain exactly what my setup is:
I have a database with two tables:
CashReceiptTable and CashReceiptItemsTable
CashReceiptItemsTable contains a FK to CashReceiptTable.
The form allows the users to add, and modify the two tables.
When the user enters a new cashreceipt, the cash receipt's id is -1, and the FK in cashReceiptitemstable is -1. When the database is saved, cashReceipt's id is updated, and I have to manually update cashreceiptitem's FK.
Here are the problems:
When I try to update the CashReceiptID (the FK) in more than one row in cashreceiteitems binding source, the first row is updated, and disappears (because it's filtered), and the other rows are removed, and I can no longer access them.
I have no idea why this is, I haven't updated the filter yet so they should still be there, but trying to access them throws RowNotInTableException.
I've managed a work around that copies the rows in the the binding source to an in memory array, deletes the first row in the binding source (all the other rows just vanish), update the row's FK and reinsert them into the binding source and save the table.
This works okay, but why do the rows disappear?
I also have one more slight problem. When the CashReceiptsTable is empty and I am adding a new row to it, if I add more than one row to the CashReceiptsItemTable it causes problems. When manually adding the items to the binding source, adding a new row pops to previous row off and pushes it onto the datatable. This hides it from my FK updating routine and it is lost, it also removes it from the DataGridView.
It only does that when I'm adding the first row to CashReceiptsTable. Why does it do this, and how can I fix it?
I'm posting my code that autopopulates it here:
private void autopopulate(decimal totalPayment) {
//remove old rows
for (int i = 0; i < tblCashReceiptsApplyToBindingSource.List.Count; i++) {
DataRowView viewRow = tblCashReceiptsApplyToBindingSource.List[i] as DataRowView;
RentalEaseDataSet.tblCashReceiptsApplyToRow row = viewRow.Row as RentalEaseDataSet.tblCashReceiptsApplyToRow;
if (row.CashReceiptsID == this.ReceiptID) {
tblCashReceiptsApplyToBindingSource.List.Remove(viewRow);
i--;
}
}
decimal payment = totalPayment;
//look for an exact amount
foreach (DataGridViewRow dueRow in dataViewDueRO.Rows) {
decimal due = -1 * (Decimal)dueRow.Cells[Due.Index].Value;
if (due == payment) {
String charge = (String)dueRow.Cells[Description.Index].Value;
int chargeID = ManageCheckbooks.findTransactionID(charge);
tblCashReceiptsApplyToBindingSource.AddNew();
RentalEaseDataSet.tblCashReceiptsApplyToRow row = ((DataRowView)tblCashReceiptsApplyToBindingSource.Current).Row as RentalEaseDataSet.tblCashReceiptsApplyToRow;
row.CashReceiptsID = this.ReceiptID;
row.ApplyTo = chargeID;
row.Paid = payment; //convert to positive
payment = 0;
break;
}
}
//if the exact amount was found, payment will = 0, and this will do nothing, otherwise,
//divy out everything left over (which will be everything)
foreach (DataGridViewRow dueRow in dataViewDueRO.Rows) {
String charge = (String)dueRow.Cells[Description.Index].Value;
decimal due = (Decimal)dueRow.Cells[Due.Index].Value;
if (due > 0 || payment <= 0) {
continue;
}
int chargeID = ManageCheckbooks.findTransactionID(charge);
payment += due; //due is negative, so this will subtract how much the user owes
tblCashReceiptsApplyToBindingSource.AddNew();
RentalEaseDataSet.tblCashReceiptsApplyToRow row = ((DataRowView)tblCashReceiptsApplyToBindingSource.Current).Row as RentalEaseDataSet.tblCashReceiptsApplyToRow;
row.CashReceiptsID = this.ReceiptID;
row.ApplyTo = chargeID;
if (payment >= 0) {
//payment is enough to cover this
row.Paid = due * -1; //convert to positive
} else {
//doesn't have enough money to conver this, can only cover partial, or none
row.Paid = (due - payment) * -1; //math:
//money remaining $50, current charge = $60
//payment = 50 + -60 = -10
//row["Paid"] = (-60 - -10) * -1
//row["Paid"] = (-60 + 10) * -1
//row["Paid"] = -50 * -1
//row["Paid"] = 50
}
if (payment <= 0) {
break; //don't conintue, no more money to distribute
}
}
isVirginRow = true;
}
And this is the function that saves it to the database:
protected override void saveToDatabase() {
tblCashReceiptsBindingSource.EndEdit();
isVirginRow = false;
RentalEaseDataSet.tblCashReceiptsRow[] rows = rentalEaseDataSet.tblCashReceipts.Select("ID < 0") as RentalEaseDataSet.tblCashReceiptsRow[];
int newID = -1;
if (rows.Count() > 0) {
tblCashReceiptsTableAdapter.Update(rows[0]);
newID = rows[0].ID;
}
tblCashReceiptsTableAdapter.Update(rentalEaseDataSet.tblCashReceipts);
//update table
/*foreach (RentalEaseDataSet.tblCashReceiptsApplyToRow row in rentalEaseDataSet.tblCashReceiptsApplyTo.Select("CashReceiptsID = -1")) {
row.CashReceiptsID = newID;
}*/
//update binding source
DataRowView[] applicationsOld = new DataRowView[tblCashReceiptsApplyToBindingSource.List.Count];
RentalEaseDataSet.tblCashReceiptsApplyToRow[] applicationsNew = new RentalEaseDataSet.tblCashReceiptsApplyToRow[tblCashReceiptsApplyToBindingSource.List.Count];
tblCashReceiptsApplyToBindingSource.List.CopyTo(applicationsOld, 0);
for (int i = 0; i < applicationsOld.Count(); i++) {
RentalEaseDataSet.tblCashReceiptsApplyToRow row = applicationsOld[i].Row as RentalEaseDataSet.tblCashReceiptsApplyToRow;
if (row.CashReceiptsID < 0) {
applicationsNew[i] = rentalEaseDataSet.tblCashReceiptsApplyTo.NewRow() as RentalEaseDataSet.tblCashReceiptsApplyToRow;
applicationsNew[i]["ID"] = row.ID;
applicationsNew[i]["CashReceiptsID"] = this.ReceiptID;
applicationsNew[i][2] = row[2];
applicationsNew[i][3] = row[3];
applicationsNew[i][4] = row[4];
//row.Delete();
}
}
for (int i = 0; i < applicationsOld.Count(); i++) {
try {
if ((int)applicationsOld[i].Row["ID"] < 0) {
applicationsOld[i].Row.Delete();
}
} catch (RowNotInTableException) {
break;
}
}
this.tblCashReceiptsApplyToBindingSource.Filter = "CashReceiptsID = " + this.ReceiptID;
foreach (DataRow newRow in applicationsNew) {
if (newRow == null) {
break;
}
tblCashReceiptsApplyToBindingSource.AddNew();
((DataRowView)tblCashReceiptsApplyToBindingSource.Current).Row[0] = newRow[0];
((DataRowView)tblCashReceiptsApplyToBindingSource.Current).Row[1] = newRow[1];
((DataRowView)tblCashReceiptsApplyToBindingSource.Current).Row[2] = newRow[2];
((DataRowView)tblCashReceiptsApplyToBindingSource.Current).Row[3] = newRow[3];
((DataRowView)tblCashReceiptsApplyToBindingSource.Current).Row[4] = newRow[4];
}
tblCashReceiptsApplyToBindingSource.EndEdit();
checkForBadRows();
tblCashReceiptsApplyToTableAdapter.Update(rentalEaseDataSet.tblCashReceiptsApplyTo);
tblCashReceiptsApplyToTableAdapter.Fill(rentalEaseDataSet.tblCashReceiptsApplyTo);
}
You might want to try adding rows to the DataGridView. Since you are binding to it, the DataGridView becomes your 'access point'.
I've got several applications that bind to DataGridView and for most circumstances when I add a row I do so through the DataGridView. It already has properties/methods/events that let you add with relative ease.
If you need some more information I can update.