GDI leak in DataGridView

GDI leak in DataGridView

Post by UGV0ZSBNb3 » Fri, 16 Feb 2007 05:01:16


I have unfortunately discovered a very bad GDI Objects leak in the
DataGridView control when binding to a DataView DataSource that uses a
RowFilter to show/hide some rows. At first I thought it was code that I was
writing to customize columns, but I have been able to reproduce it with a
100-line WinForms app with no customization.

I have a DataTable with 8500 rows in it. One of the DataColumns is a bool,
called "Visible". I set my DefaultView as follows:
tbl.DefaultView.RowFilter = "Visible = true"

The DataTable is initially populated such that only the 1st 100 rows and
last 500 rows have Visible = true. I then bind to the DataGridView.
Everything is find and the GDI Object count is stable at around 50 objects.

I then click a CheckBox which toggles the Visible value of rows between
[100, 8000) to true:
private void ShowAllDataRows(bool bShow)
{
for (int n = 100; n < 8000; n++)
{
m_tbl.Rows[n][1] = bShow;
}
}
The DataGridView is a bit slow but after a few seconds it shows all the
rows. The GDI object count does not even change. So far so good.

Now simply pick up the scroll bar and zing it around to scroll back and
forth for a while. Watch the GDI Objects count march on upwards until it hits
10,000, which we all know is the magic number at which point the app dies a
horrible death.

The count never goes down, and manual attempts to perform GC do not reduce
the count.

Please -- someone at Microsoft. Can you respond with why this is happening,
and is there a fix? If not, is there any workaround that can be used? I will
happily provide the trivial sample app if you tell me where to send it.
 
 
 

GDI leak in DataGridView

Post by Clay » Fri, 16 Feb 2007 06:10:42

One other observation. This code also avioded the problem for me. It
looks like a cached row changes are causing the behavior. Calling
DataTable.AcceptChanges seem to manage it ok.

private void checkBox1_CheckedChanged(object sender, EventArgs
e)
{
for (int n = 100; n < 8000; n++)
{
dt.Rows[n]["bool"] = show;
}
dt.AcceptChanges();
show = !show;
}

=================
Clay Burch
Syncfusion, Inc.

 
 
 

GDI leak in DataGridView

Post by Clay » Fri, 16 Feb 2007 06:15:52

One other observation. This code also avioded the problem for me. It
looks like a cached row changes are causing the behavior. Calling
DataTable.AcceptChanges seem to manage it ok.

private void checkBox1_CheckedChanged(object sender, EventArgs
e)
{
for (int n = 100; n < 8000; n++)
{
dt.Rows[n]["bool"] = show;
}
dt.AcceptChanges();
show = !show;
}

=================
Clay Burch
Syncfusion, Inc.
 
 
 

GDI leak in DataGridView

Post by UGV0ZSBNb3 » Fri, 16 Feb 2007 07:59:10

Clay,

Thanks for the tip. The first suggestion did not work. That is
dt.Rows[n][1] = show
is equivalent to
dt.Rows[n]["bool"] = show
No difference in my app.

However, calling dt.AcceptChanges() following the for loop did, in fact,
cause the GDI leaks to disappear. I have opened an issue with MS on this. I'd
like to hear why this occurs to avoid issues like this in the future. If I
learn anything from them, I'll post an update here.

As a followup question/issue, I'd like to be able to defer any DataGridView
repainting during the loop. How could one do that? Something like a
BeginUpdate()/EndUpdate() kind of thing, but I couldn't see anything obvious
there.

Again, thanks for the tip.
Peter
 
 
 

GDI leak in DataGridView

Post by Clay » Fri, 16 Feb 2007 09:26:16

The first suggestion was to set the DaatSource = null and then reset
it after flipping the bool values.

this.dataGridView1.DataSource = null;

//looping code

this.dataGridView1.DataSource = dt;

But the call to AcceptChanges is probably a better work-around.

Here is a FAQ link that shows how you can freeze the painting of any
control. The code sets up a FreezePainting property that you can turn
off/on. To use it with a control, you can derive the control class and
add this property to the derived class.

http://www.yqcomputer.com/

==============
Clay Burch
Syncfusion, inc.
 
 
 

GDI leak in DataGridView

Post by Clay » Fri, 16 Feb 2007 09:33:23

In addition to the FreezePainting code mentioned above, here is
another thing you might try. Somehow, call suspendbinding on the
datasource. If you are using a BindingSource, you could try calling
BindingSource.SuspendBinding/ResumeBinding. Another way to do this is
through the CurrencyManager which also has a SuspendBinding/
ResumeBinding methods. To get the CurrencyMaanger, you can use code
like:

CurrencyManager cm = grid.BindingContext[grid.DataSource,
grid.DataMember] as CurrencyManager;

The FreezePainting will suspend the drawing of the control, whereas
the SuspendBinding will avoid the grid reacting to
IBindingList.ListChanged events.

=================
Clay Burch
Syncfusion, Inc.