I am trying to update an asp:ListView once after some conditions on the items are true. The problem is that the update happens on the loop and therefore I end up adding the wrong values.
This is my asp:listview:
Item 1 Item 2 Item 3 Item 4 ERROR
john 490 1 0 Message
peter 4 0 0 Message
veronica 2 3 1 Message
Oscar 2 0 0 Message
Caroline 1 0 0 Message
The conditions are as follow:
1.- I have a total number of 499
2.- The total number of Item 2 (sum of all rows) must match exactly 499
3.- Item 2 cannot be lower than the result of minus Item 3 with Item 4
4.- Once all the conditions are correct, the update must be done globally for all rows in the listview.
This is what I am doing:
<asp:label id="lbl_error_outside_view" runat="server"/>
<asp:ListView ID="gv_browse" runat="server" AutoGenerateColumns="False">
...
</asp:ListView>
<asp:Button ID="btnupdate" Text="Update All" runat="server" OnClick="btnupdate_Click" />
protected void btnupdate_Click(object sender, EventArgs e)
{
int total_at = 499;
int total_global = 0;
int total_items = 0;
//Get total value for all Item2 column = total_global
foreach (ListViewItem itemRow in this.gv_browse.Items)
{
var item2 = itemRow.FindControl("tbx_item2") as TextBox;
int item2_int = Convert.ToInt32(item2.Text);
total_global = total_global + item2_int;
}
foreach (ListViewItem itemRow in this.gv_browse.Items)
{
var item1 = itemRow.FindControl("hdn_item1") as HiddenField;
var item2 = itemRow.FindControl("tbx_item2") as TextBox;
var item3 = itemRow.FindControl("lbl_item3") as Label;
var item4 = itemRow.FindControl("lbl_item4") as Label;
Label lbl_error = itemRow.FindControl("lbl_error") as Label;
int result_item3_4 = (Convert.ToInt32(item3.Text) - Convert.ToInt32(item4.Text));
int item2_int = Convert.ToInt32(item2.Text);
total_items = total_items + item2_int;
if (item2_int < result_item3_4)
{
//ERROR, DO NOT UPDATE
lbl_error.Text="...";
}
else if (item2_int >= result_item3_4)
{
lbl_error.Text = "---"; //Clear error message
}
if (total_items > total_at)
{
//ERROR, DO NOT UPDATE
lbl_error_outside_view.Text ="...";
}
else if (total_global < total_at)
{
//ERROR, DO NOT UPDATE
lbl_error_outside_view.Text="...";
}
}
My question is where I put the update line exactly?
//Update all in the DB
UpdateData(item1, item2);
If I call the UpdateData method in the foreach, I end up updating only the row(s) that are comply with the conditions. What I need is to be able to update all the rows ONLY after verifying that each row is correct? All the conditions work although most probably there is a better way of doing them. Could anyone see what I am doing wrong? Thank you so much.
UPDATE
Let me clarify why an ELSE inside the foreach loop does not work:
I populate the listview from the database with several different rows. I added 5 rows as an example. Each row has 3 columns with numerical values, item2, item3 and item4 columns. I have a global number, for this example, I said 499. The sum of item2 in all rows must much exactly this global number, 499. If it doesn't, then the update should not happen (i.e. error shows in the lbl_error_outside_view label). Therefore, here is when the first condition starts: "if total of all rows not equal to global number...".
Now, for the second condition, I need to check each individual row and make sure that Item2 column is not lower than (item3 - item4) columns. If it doesn't, then the update should also not happen (i.e. error shows in the lbl_error label for that particular row). Because I am inside the foreach loop, and because I am checking two different things, sum of total number of item2 rows and then individual rows, I cannot add the else { updateData(item1, item2); } because that will basically just update the row that the condition applies which is not what I need. That is why an else on the loop is not the solution.
All rows should be updated at once ONLY after we know that total number of item2 in all rows is equal to 499 AND each row in item2 is not lower than (item3-item4).
If anyone could share some insides that would be great thanks a lot
After several testing I found the best solution to my problem. Basically the solution was in front of my eyes but I was not able to see it until now.
foreach (ListViewItem itemRow in this.gv_browse.Items)
{
//run all the conditions here and add ONLY the correct results to a DataTable (if wrong result found, return;)
}
then I call the datatable and do the update here.
foreach (DataRow row in dt.Rows)
{
//call update method
}
In the end, was a pretty simple thing but it took me a while to realize that. I hope this helps someone else that faces the similar problem.
Related
So I have this DataGridView
And This code:
if(dataGridView1.SelectedRows.Count > 1)
{
MessageBox.Show("Error: More than one value selected");
return false;
}
It counts correctly to 2 if I have 2 completly selected rows.
But I want to check if any cells from 2 different rows or more are selected.
In other words:
in my picture my current selection is returning 1 at the moment but i want it to return 2.
Thank you.
Edit after fix: Working code:
if(dataGridView1.SelectedCells.Cast<DataGridViewCell>().Select(c => c.RowIndex).Distinct().Count() > 1)
{
MessageBox.Show("Error: More than one value selected");
return false;
}
To get the number of rows with selected cells :
int count = dataGridView1.SelectedCells.Cast<DataGridViewCell>()
.Select(c => c.RowIndex).Distinct().Count();
To check if more than one row is selected:
var selectedCells = dataGridView1.SelectedCells;
bool check = selectedCells.Count > 0
&& selectedCells.Cast<DataGridViewCell>().Any(c => c.RowIndex != selectedCells[0].RowIndex);
If you have a fairly standard setup where each column is bound to a property of your backing data object, and each row represents one object, the following should work:
dataGridView1.SelectedCells.Select(c => c.Item).Distinct().Count()
This will return the number of items the different cells are bound to. Since each item has one row that binds to it, it will return the count of every row with at least one selected cell.
I made a code for delete the selected row, you can see here:
var grid = Players_DataGrid;
var mygrid = Players_DataGrid;
if (grid.SelectedIndex >= 0)
{
for (int i = 0; i <= grid.SelectedItems.Count; i++)
{
mygrid.Items.Remove(grid.SelectedItems[i]);
};
}
grid = mygrid;
But there's a problem. If the user select multiple with ctrl combination rows the program crash displaying this exception:
Argument out of range exception
on mygrid.Items.Remove(grid.SelectedItems[i]);
Is my code wrong? Isn't the best way to delete values?
you delete an item from the list you are iterating through.
Let's say your list has 10 items so you have an for loop from 0 to 9. If you delete 2 Items you still will iterate to 9 and the list has only 8 items so you get an:
Argument out of range exception
you can solve this by iterating backwards
for (int i = grid.SelectedItems.Count -1; i >= 0; i--)
Edit:
the removed item will be removed from grid.SelectedItems too.
I want to clear the datagridview starting from the row which user chose. I am trying a few ways to implement it, but they all generate some errors, now I have something like this:
foreach(DataGridViewRow row in this.dataGridView1.Rows)
{
if(row.Index >= odelementu - 1)
dataGridView1.Rows.RemoveAt(row.Index);
}
User need to choose from which row we should clear the datagridview and then click the button
odelementu //this variable represents the starting row
I don't know why the loop misses some rows. I would be grateful for any advices
It's because you modify the DataGridViewRowCollection in the foreach loop and make the index gotten becomes inexact. Try this instead:
while(dataGridView1.Rows.Count>=odelementu) {
dataGridView1.Rows.RemoveAt(odelementu-1);
}
public void RemoveRows()
{
var selectedrows = datagridview.SelectedRows.Cast<DataGridViewRow>();
if (selectedrows.Count() == 0)
return;
var fromIndex = selectedrows.Last().Index;
datagridview.Rows.Cast<DataGridViewRow>()
.Where(p=>p.Index > fromIndex)
.ToList()
.ForEach(p=>datagridview.Rows.Remove(p));
}
I've a datagridview in which values are inserted.The gridview is like this.
Item PRID
------ ------
Item1 1
Item2 2
Item3 2
I am trying to compare the PRID with a variable which holds the selected row PRID.
What I've done so far.
foreach (DataGridViewRow dgv_r in PurchaseOrder_dgv.Rows)
{
if (dgv_r.Cells[1].Value.ToString() == CurrentSelected_PRID_ForPurchaseOrder.ToString())
{
PurchaseOrder_dgv.Rows.Remove(dgv_r);
}
}
But it deletes the bottom row not the second row.and gives the following error.What I want is if the value of CurrentSelected_PRID_ForPurchaseOrder is equal to 2 then it should delete both the rows.I've tried it using for loop also but it gives me Index out of range error.It is giving the following error.
Object Reference Not set to an instance of object
The are a couple of ways around this. One is to do the following
for (int i = dataGridView1.RowCount - 1; i >= 0; i--)
if (String.Compare(dataGridView1.Rows[i].Cells[1].Value.ToString(), "2") == 0)
dataGridView1.Rows.Remove(dataGridView1.Rows[i]);
This is looping from the bottom end of the DataGridView and avoids the problem with removing rows whilst iterating.
I hope this helps.
As mellamokb pointed out the reason is because your editing the collection during the foreach. One solution would be to store the rows with the same PRID in a list first and then remove them after. Something like this
var rowsToRemove = new List<int>();
foreach (DataGridViewRow dgv_r in PurchaseOrder_dgv.Rows)
{
if (dgv_r.Cells[1].Value.ToString() == CurrentSelected_PRID_ForPurchaseOrder.ToString())
{
rowsToRemove.Add(dgv_r.Index);
}
}
rowsToRemove.Reverse();
rowsToRemove.ForEach(i => PurchaseOrder_dgv.Rows.RemoveAt(i));
Another solution is to use a while loop instead which takes into account the possiblity of the collection size changing.
int i = 0;
while (i < PurchaseOrder_dgv.Rows.Count)
{
if (PurchaseOrder_dgv.Rows[i].Cells[1].Value.ToString() == CurrentSelected_PRID_ForPurchaseOrder.ToString())
{
PurchaseOrder_dgv.Rows.RemoveAt(i);
}
i++;
}
I have a DataGridView and when I select multiple rows, I want the index of the last selected row. In other words, how to get the maximum most index from a selection of rows.
e.g., if I select row0, row1 and row6, I want the output as "6".
Regards.
if (dataGridView1.SelectedRows.Count > 0)
{
int lastIndex = dataGridView1.SelectedRows[dataGridView1.SelectedRows.Count - 1].Index;
}
var x = dataGridView1.SelectedRows.Cast<DataGridViewRow>().Max(row => row.Index);
is the same to:
var y = dataGridView1.SelectedRows.Cast<DataGridViewRow>().Last().Index;
Sorry, I'm adding answer for myself. There could be other faster ways, but this works.
List<int> lst = new List<int>();
foreach (DataGridViewRow row in dg.SelectedRows)
lst.Add(row.Index);
lst.Sort();
int i = lst[lst.Count - 1];
What this does is add the indexes of all selected rows to an List<> and then do a sort and then give the last item from the sorted List<>.
Note: the problem with Bala R's method is that, it depends on the order the rows were selected (ie, where the selected pointer lies). It fails when selected rows aren't in an order. It gives the row that was selected last, not necessarily the maximum most index from a selection of rows..
Thanks everyone!