I have a WinForm app with multiple DataGridViews bound to SortableBindingLists.
Under some circumstances, I need to programmatically delete an item from the list that grid is bound to.
I can't seem to get the DGV to recognize that it's data has changed, or, specifically, that it has fewer rows. I'm calling dataGridView1.Invalidate(), and it's repainting the grid, but it tries to repaint as many rows as their were before, and throws a series of exceptions that "Index does not exist", one exception for each column.
Here's a simplified code sample that exhibits the problem: (just a WinForm with a DGV and a button.)
private List<Employee> list;
private void Form1_Load(object sender, EventArgs e)
{
list = new List<Employee>();
for (int ix = 0; ix < 3; ix++)
{
list.Add(ObjectMother.GetEmployee(ix+1));
}
dataGridView1.DataSource = list;
}
private void cmdDeleteARow_Click(object sender, EventArgs e)
{
list.Remove(list[0]);
dataGridView1.Invalidate();
}
In ASP.NET, when using a GridView control, there's a "DataBind()" method you can call to force it to refresh it's data. There does not seem to be any such thing in WinForms, or am I missing something?
In order for a DataGridView
to pick up on changes to its DataSource
, the source should implement IBindingList. List<T>
doesn't, so it doesn't broadcast its changes, and the DataGridView
doesn't know it needs to be updated.
An easy fix in this case is to put a BindingSource between the list and the DataGridView
, and then call Remove()
on it instead:
private List<Employee> list;
private BindingSource bindingSource;
private void Form1_Load(object sender, EventArgs e)
{
list = new List<Employee>();
for (int ix = 0; ix < 3; ix++)
{
list.Add(ObjectMother.GetEmployee(ix+1));
}
dataGridView1.DataSource = bindingSource;
bindingSource.DataSource = list;
}
private void cmdDeleteARow_Click(object sender, EventArgs e)
{
bindingSoruce.Remove(list[0]); // or, RemoveAt(0)
// Probably not necessary:
// dataGridView1.Invalidate();
}
Alternatively, you could use BindingList<T>
instead of List<T>
, or create your own list class that implements IBindingList
.
Well, since I'm not getting any useful responses, I'm going to go ahead and use the kludge I've come up with.
If you use reflection to go into the DataGridView.DataSource property, you'll see that the binding methods are only called if the DataSource changes. Note that a change to the contents of the DataSource (e.g, adding, changing, or deleting a list element) are not recognized as a change to the DataSource. In order to force the data binding methods to be called, what I have done successfully is to reassign the DataSource to some other object, then assign it back to the list. Seems incredibly kludgy, and a monumental waste of CPU cycles, but it appears to work. So the code becomes:
private void cmdDeleteARow_Click(object sender, EventArgs e)
{
list.Remove(list[0]);
dataGridView1.DataSource = new List<Employee>();
dataGridView1.DataSource = list;
dataGridView1.Invalidate();
}
If anyone has any better ideas (and I'm sure there have to be some out there), please let me know.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With