• Coding
  • [.NET] Deleting A Row In a DataGridView

Hey guys,

I'm trying to find a way to delete a selected row from a DataGridView control. I've added two columns using the designer, one that should delete the row, and another one should allow me to edit the row. Then I'm getting the data from my model (using LINQ and Entity Framework) and binding the result as the data source (ProductsDGV.DataSource = QueryResult)

Anyway, now I need to place some code which does the trick inside the click handler of that delete button. I tried doing some stuff similar to what I usually do with ComboBoxes (using the SelectedIndex property) but that didn't work and I got stuck eventually... So could someone help me with some code to get started please?
Kassem, try dgv.CurrentRow instead of SelectedIndex.
xterm wroteKassem, try dgv.CurrentRow instead of SelectedIndex.
Actually I got to the current row using "more code", your code is much cleaner and straight to the point. But honestly I did not know where to go from there. This returns a DataGridViewRow object, but what I'm looking for is a way to get to the Product object or at least the Name or ProductID properties in that row. Then I could simply delete it from the database.
Oh! I think it's DataBoundItem

dgv.CurrentRow.DataBoundItem

Then just cast it.
xterm wroteOh! I think it's DataBoundItem

dgv.CurrentRow.DataBoundItem

Then just cast it.
I actually tried that before but without casting the object and it didn't work at all. Now that I read your post, I tried to cast it but I got the following error:
System.InvalidCastException was unhandled
Message=Unable to cast object of type '<>f__AnonymousType0`5[System.String,System.String,System.Double,System.Double,System.Int32]' to type 'HarbManager.Product'.
Also, another issue arose when adding new products although it was working fine. I think the problem happened when I added edit and delete columns to the DGV control. I keep getting an error that asks me to see the Inner Exception for more details, how can I catch the Inner Exception?

This is my code for reference:
private void SaveBtn_Click(object sender, EventArgs e)
        {
            int index = CategCB.SelectedIndex;
            string categName = (string)CategCB.Items[index];
            var categ = harbDB.Categories.Single(c => c.Name == categName);
            
           // Validation code here

                try
                {
                    var product = new Product
                    {
                        Name = PName.Text,
                        CategoryID = categ.CategoryID,
                        Cost = double.Parse(CostTB.Text),
                        Price = double.Parse(PriceTB.Text),
                        AmountInStock = int.Parse(AmountTB.Text),
                        Description = DescriptionTB.Text
                    };
                    harbDB.AddToProducts(product);
                    harbDB.SaveChanges();
                    ClearFields();
                    MessageBox.Show("Product was added successfully!", "Success");
                    Helper.PopulateProducts(AdminMain.ProductDataGrid);
                    this.Close();  
                }
                catch (EntitySqlException ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }
InnerException

You could expand the "InnerException" in the exception dialog when you're debugging through visual studio to see the inner exception or you could use ex.InnerException, though one thing you need to know is that InnerExceptions can go very deep.

InvalidCastException

Yes, that is expected. I figured you had your DataSource populated with the actual Product entries but apparently you've projected the data onto an IEnumerable of Anonymous Types.

Example:
 var products = from p in db.Products where p.Price > 300 select new { Name = p.Name, Quantity = p.Quantity };
products will thus be an enumerable of an anonymous type which was generated using new {...}, which is why you weren't able to cast the data bound item to Product.

Solution

Instead of Selecting new { }, just select p.

Alternative

If your model is quite large and you really want to project to a type, make it a static type like so:
public class ProductVM {
  public int ID { get; set; }
  public String Name { get; set;}
}
and in your query:
 var products = from p in db.Products select new ProductVM { ID = p.ProductID, Name = p.Name }
Then when you get the DataBoundItem cast it to ProductVM instead of Product.

P.S.: VM stands for ViewModel
Is this what you're looking for :
// Get the Row index of the selected cell
int index = dataGridView.CurrentCell.RowIndex;

// Deletes the Row from the Datagridview
dataGridView.Rows.RemoveAt(index);
Kassem wrote...but what I'm looking for is a way to get to the Product object or at least the Name or ProductID properties in that row. Then I could simply delete it from the database.
// Get the Value of the cell at a specified index, in this case the column "Name"
dataGridView.Rows[index].Cells["Name"].Value.ToString();
or
// The same for the column "ProductID".
dataGridView.Rows[index].Cells["ProductID"].Value.ToString();
@xterm:
Apparently you were right! But now it's telling me the object cannot be deleted because it was not found in the ObjectStateManager and I have no idea what that is lol. I'll look it up in Google.

And about that Inner Exception error, I found out that it's because the ProductID doesn't Auto Increment because I forgot to set the Identity property to true (actually I did not know I should - makes me miss the good days of MySQL and phpMyAdmin :) ). So now I either have to find a way to alter the tables, or I'll have to re-create the whole database right from the start... *sighs*

Thanks for the help, much appreciated :)

@Yorgi: I'll give that a shot. Hopefully it's going to do the trick!
In reply to Yorgi.

That would work but it would over-shadow the benefits of data binding. As opposed to:
var p = (from n in db.Products where p.ID == (dgv.CurrentRow.DataBoundItem as ProductVM).ID select n).First();
db.Products.DeleteOnSubmit(p);
db.SubmitChanges();
dgv.Update(); // or refresh, i don't know.
Both work anyways.
Kassem wrote@xterm:
Apparently you were right! But now it's telling me the object cannot be deleted because it was not found in the ObjectStateManager and I have no idea what that is lol. I'll look it up in Google.

And about that Inner Exception error, I found out that it's because the ProductID doesn't Auto Increment because I forgot to set the Identity property to true (actually I did not know I should - makes me miss the good days of MySQL and phpMyAdmin :) ). So now I either have to find a way to alter the tables, or I'll have to re-create the whole database right from the start... *sighs*

Thanks for the help, much appreciated :)

@Yorgi: I'll give that a shot. Hopefully it's going to do the trick!
in your lookup query use .First() at the end, check my previous post to yorgi.

Remember, most of the constructs in LINQ Queries return IEnumerable<T> (or IQueryable<T>)
Ok now Yorgi's method works, so thanks a bunch! :)

But just like xterm pointed out, that would over-shadow the benefits of Data Binding. I'll try using the .First() method and see if that would work. Will report back with the result.
Unfortunately, I couldn't get it to work so I'll just stick to Yorgi's method.

I tried to alter the table and add the AUTO INCREMENT property but it didn't work. Here's the SQL statement I've tried:
ALTER TABLE Products CHANGE ProductID 
ProductID int( 4 ) UNSIGNED UNIQUE NOT NULL AUTO_INCREMENT PRIMARY KEY
Does anyone have a solution to this?
xterm wroteIn reply to Yorgi.
That would work but it would over-shadow the benefits of data binding.
Both work anyways.
True...
a month later
Kassem wroteHey guys,

I'm trying to find a way to delete a selected row from a DataGridView control. I've added two columns using the designer, one that should delete the row, and another one should allow me to edit the row. Then I'm getting the data from my model (using LINQ and Entity Framework) and binding the result as the data source (ProductsDGV.DataSource = QueryResult)

Anyway, now I need to place some code which does the trick inside the click handler of that delete button. I tried doing some stuff similar to what I usually do with ComboBoxes (using the SelectedIndex property) but that didn't work and I got stuck eventually... So could someone help me with some code to get started please?
you can do following solution,

DataGridViewRow row = dataGridView.SelectedRows[index];//pass index that you would like to remove.
dataGridView.Rows.Remove(row);