views:

5529

answers:

9

I've actually solved this, but I'm posting it for posterity.

I ran into a very odd issue with the DataGridView on my dual-monitor system. The issue manifests itself as an EXTREMELY slow repaint of the control (like 30 seconds for a full repaint), but only when it is on one of my screens. When on the other, the repaint speed is fine. I have an nVidia 8800 GT with the latest non-beta drivers (175. something). Is it a driver bug? I'll leave that up in the air, since I have to live with this particular configuration. (It does not happen on ATI cards, though...)

The paint speed has nothing to do with the cell contents, and custom drawing doesn't improve the performance at all - even when just painting a solid rectangle.

I later find out that placing a ElementHost (from the System.Windows.Forms.Integration namespace) on the form corrects the problem. It doesn't have to be messed with, it just needs to be a child of the form the DataGridView is also on. It can be resized to (0, 0) as long as the Visible property is true.

Not wanting to explicitly add the .Net 3/3.5 dependency to my app, I make a method to create this control at runtime (if it can) using reflection. It works, and at least it fails gracefully on machines that don't have the required library - it just goes back to being slow.

This method also lets me apply to fix while the app is running, making it easier to see what the WPF libraries are changing on my form (using Spy++).

After a lot of trial and error, I notice that enabling double buffering on the control itself (as opposed to just the form) corrects the issue!


So, you just need to make a custom class based off of DataGridView so you can enable its DoubleBuffering. That's it!

class CustomDataGridView: DataGridView
{
    public CustomDataGridView()
    {
        DoubleBuffered = true;
    } 
}

As long as all of my instances of the grid are using this custom version, all is well. If I ever run into a situation caused by this where I'm not able to use the subclass solution (if I don't have the code), I suppose I could try to inject that control onto the form :) (although I'll be more likely to try using reflection to force the DoubleBuffered property on from the outside to once again avoid the dependency).

It is sad that such a trivially simple thing ate up so much of my time...

A: 

Well done. In effect you are saying enabling Double Buffering solved a weird multi-display speed issue.

You don't say but was the problem on the Primary or Secondary Screen?

Brody
Between Windows' and nVidia Control Panel's settings, I'm sort of unsure which was actually the primary display. It is the screen that Windows identified as '1' (which is not the screen that has my taskbar).
Corey Ross
A: 

Interesting issue. Do you think that the use of double buffering actually fixes the problem, or just means that rather than 30 seconds of flickering, you see one clean update after 30 seconds?

Chris Ballard
It definitely fixed the issue. It meant the difference between being able to use the scrollbars and not.
Corey Ross
+12  A: 

You just need to make a custom class based off of DataGridView so you can enable its DoubleBuffering. That's it!


class CustomDataGridView: DataGridView
{
    public CustomDataGridView()
    {
        DoubleBuffered = true;
    } 
}

As long as all of my instances of the grid are using this custom version, all is well. If I ever run into a situation caused by this where I'm not able to use the subclass solution (if I don't have the code), I suppose I could try to inject that control onto the form :) (although I'll be more likely to try using reflection to force the DoubleBuffered property on from the outside to once again avoid the dependency).

It is sad that such a trivially simple thing ate up so much of my time...

Note: Making the answer an answer so the question can be marked as answered

Benoit
How can you do this with Windows Forms Integration for WPF??
Partial
A: 

We've experienced a similar problem using .NET 3.0 and DataGridView on a dual monitor system.

Our application would display the grid with a gray background, indicating that the cells could not be changed. Upon selecting a "change settings" button, the program would change the background color of the cells white to indicate to the user that the cell text could be changed. A "cancel" button would change the background color of the aforementioned cells back to gray.

As the background color changed there would be a flicker, a brief impression of a default sized grid with the same number of rows and columns. This problem would only occur on the primary monitor (never the secondary) and it would not occur on a single monitor system.

Double buffering the control, using the above example, solved our problem. We greatly appreciated your help.

A: 

Just to add what we did to fix this issue: upgraded to the latest NVIDIA drivers solved the problem. No code had to be rewritten.

For completeness, the card was an NVIDIA Quadro NVS 290 with drivers dated March 2008 (v. 169). Upgrading to the latest (v. 182 dated Feb 2009) significantly improved the paint events for all my controls, especially for the DataGridView.

This issue was not seen on any ATI cards (where development occurs).

Richard Morgan
A: 

Hi

I'm kind of new in c# and I'm experiencing the same flickering problem with datagridview in my application. Could you please explain where to add this code in the application? In my application there are 2 classes: Main.cs and Form1.cs (in which resides de datagridview). Where should I put the code you mention before?

Thanks in advance!

You can place this in an additional .cs file in your project. It should even show up in your form designer toolbox (or you can edit the designer code - if you don't want to rewire a bunch of events - to change the type of the field declaration and the type used when it is instantiated).
Corey Ross
A: 

How do you implement this for WPF in XAML or in C#/VB.NET when your using WindowsFormsIntegration?

Partial
+4  A: 

Here is some code that sets the property using reflection, without subclassing as Benoit suggests.

typeof(DataGridView).InvokeMember(
   "DoubleBuffered", 
   BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
   null, 
   myDataGridViewObject, 
   new object[] { true });
Brian Ensink
thank you, very useful
marco.ragogna
Glad to help! I almost didn't post it because this question was already a year old.
Brian Ensink
A: 

Hi everyone,

I found a solution to the problem. Go to troubleshoot tab in the advanced display properties and check the hardware acceleration slider. When I got my new company PC from IT, it was set to one tick from full and I didn't have any problems with datagrids. Once I updated the video card driver and set it to full, painting of datagrid controls became very slow. So I reset it back to where it was and the problem went away.

Hope this trick works for you as well.

John Zhu