Windows Develop Bookmark and Share   
 index > Windows Forms Data Controls and Databinding > DataGridView.CellPainting -- when is this thrown?
 

DataGridView.CellPainting -- when is this thrown?

Greetings,

I have a DataGridView for which I am trying to improve the performance -- it has a lot of data that is rapidly updating, some of the cells are formatted depending on the value within the cell (or some other, outside factor). Because of this, I handle the DataGridView.CellPainting event and do some custom drawing (sometimes I draw a box around the cell, sometimes I change the Forecolor of the text, etc.).

I assumed that this event was thrown (a) whenever the cell's content changed, or (b) whenever the cell was made visible from an invisible state (e.g. you scroll the DGV and a cell is scrolled into view).

But it appears that this event is being called in some other case -- the cell's value isn't changing, and I'm not even touching the DataGridView, yet occasionally I'll get a CellPainting event thrown for the static cell in question.

I think if I can solve this issue, I will be able to greatly improve the performance of my DataGridView, especially when there are hundreds of rows. Thanks.

Rick
slikrik  Tuesday, May 05, 2009 5:05 PM
The CellPainting event is called whenever the cell needs to be repainted. Things that can cause this are the cell needing to redraw because it was invalidated, covered by another window, the data source changes, the cell value changes, cell came into view after being scrolled, etc..

If the datagridview is just sitting there the cell should not have to redraw. Are you doing something in a timer?


Silverlight-Help.com
Ken Tucker  Tuesday, May 05, 2009 6:12 PM
Those were my initial assumptions as well, that whenever the cell needed to be repainted (for any logical reason, including those you listed), this event would be fired.

However, I can't seem to put a finger on why it's being called in my case.

CONTRACT    QTY     BID     ASK     QTY   VOLUME
================================================
IBM        1239  106.18  106.19   10742  132,423

What's happening is this: *sometimes* when I change one of the QTY columns, the CellPainting event is thrown for all columns, even if I'm doing nothing to the DGV. What's weird though is that it doesn't happen *every* time one of the QTY columns changes, I'd say probably half the time it causes all cells to repaint. What's even stranger is that when the VOLUME column changes, this never happens. There is something special about the two QTY columns that, when their values change, it causes all cells to repaint.

The only difference so far as I can tell between the QTY columns and all the other ones is their color. They aren't tied anyhow to the other cells, so I'm not sure why changing their values forces everyone else to repaint, and for that matter why it only happens some of the time. I have no timers of any sort. When I comment out the lines that update the QTY columns, the CellPainting event is never fired (unless one of the cases you listed above occurs).

Rick
slikrik  Wednesday, May 06, 2009 5:01 PM
What are you bound to? A datatable, list, bindingsource, bindinglist
Silverlight-Help.com
Ken Tucker  Wednesday, May 06, 2009 6:24 PM
My DataGridView is not bound to anything. I manually update each cell. e.g:

myDGV.Rows[i].Cells[m_BidQtyColumn.Index].Value = commodity[i].GetBidQty();
myDGV.Rows[i].Cells[m_AskQtyColumn.Index].Value =commodity[i].GetAskQty();;

doing this is what causes all the other cells in that row (i) to repaint. If I comment out the two lines above, the cells do *not* repaint.
slikrik  Wednesday, May 06, 2009 6:32 PM
Really havent played with the datagridview when it is unbound. I would maybe try calling the DataGridView's SuspendLayout method before you change the cell values and ResumeLayout after you are done changing the values. Maybe that will help.
Silverlight-Help.com
Ken Tucker  Wednesday, May 06, 2009 6:41 PM
One other thing I should mention is the fact that the Bid_Qty and Ask_Qty columns (the ones that are causing this 'trouble') are the only columns that have foreground colors other than the row's default foreground color. Not sure why that would cause the other cells to repaint, but it's worth mentioning.
slikrik  Wednesday, May 06, 2009 6:44 PM
I am having the same problem with the DataGridView as you are having! Although I have up to 6 grids with 5 columns and up to 45 rows! That's as many as 1350 cells on the screen at one time. Most of the cells remain static but some of them can change rapidly as the incoming data gets updated in real time from the exchange. As cells change value, the BackColor is also changed according to price movement which makes it easier to take in at a glance! Even though a small fraction of the cells are being updated, it appears all cells are repainted which slows the whole thing down. The ClipBounds property indicates the area that needs to be repainted. Don't know if this helps!, but I'm running out of ideas!
john1166  Thursday, May 14, 2009 12:28 PM
glad to see I'm not the only one experiencing this (and for the record, I still have not come up with a solution). Unfortunately though, as confident as I am that this is not the intended behavior, if Mr. Tucker has never heard of this occurring, something tells me we aren't going to find a solution here.

FYI - Do you do your cell formatting (changing of the background color, etc.) in the CellPainting method or do you simply change the Cell's BackgroundColor property? If you do the latter, I suggest you do the former, because explicitly changing the fore/background colors causes a ridiculous amount of slowdown. So whenever I do my custom formatting in real time (like you said, based on market data, direction, etc.) I always just do it in code in the CellPainting event handler.
slikrik  Thursday, May 14, 2009 12:42 PM
I Do agree with you on handling the CellPainting rather than changing the property directly it's just that in the CellPainting event you still have to take care of the cells that don't need updating as well! By this I mean if the particular cell you are working on does not need to be repainted, you've got 1 of 2 choices to make.

One, don't do anything which means e.handled = false, and the cell will get repainted anyway.
Two, don't do anything but make e.handled = true. In the latter case, resizing your window, scrolling or moving another window across your app will mess up the cells that are not updated!

If you could have two different CellPainting Event handlers for different types of triggers for example; only invalidate the whole grid if a form resize, scroll or part of the grid has been uncovered event occurs (not to sure about that last one!). And when your app has complete focus use a different CellPainting Event handler that just updates what you want to update!

john1166  Thursday, May 14, 2009 1:29 PM
It's amazing, I have spent the last 2 nights Googling to try and find a solution and I can't believe that this is not a common problem with the performance of the DataGridView!
john1166  Thursday, May 14, 2009 1:54 PM
I've just found a couple of links which showed promise in solving this problem but alas, it was not to be! One of them had an idea to get rid of the grid lines, this improved performance by 50 % but wasn't very pleasing to the eye. Anyway I could still see flicker in cells that had values that were not changing. Not too sure if I can post links here but here goes. Below are the links I've just found:

http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.windowsforms.controls/2006-02/msg00191.html

http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic56547.aspx


john1166  Thursday, May 14, 2009 5:26 PM
This isn't a solution to our problem, but I think I can offer a solution to your flicker (the cell will still repaint, which we're trying to fix, but it won't flicker).

In your CellPainting method, rather than:

1) draw the background
2) draw the grid line
3) draw the contents

directly to the Graphics object of the DGV, instead create a bitmap of equal size, do all the drawing to the *bitmap* and then draw the completed bitmap to the cell. The reason you're probably seeing flicker is because of drawing the background first, which effectively erases the contents inside the cell, and then drawing the contents again. Using the bitmap method is akin to double-buffering, and it will reduce your flicker.

HTH
slikrik  Thursday, May 14, 2009 5:32 PM
Sorry, I forgot to mention that the flicker is not at all bad and is not the problem. The fact that I can see a bit of flicker tells me that there is unnecessary painting going on!
With over 1300 Grid cells on the screen from 6 data grids, I would say most of the time only 10 to 20 cells need to be modified a few times every second, the rest should remain untouched. That's about 98% unnecessary overhead! Occasionally there might be a burst of complete columns being lit up, but this happens rarely.

I have written 2 programs using multiple DataGridViews and they are created programmatically, 1 is used in real time when I am connected to the exchange and the other is used to play back the data at another time allowing me to move through the data a lot quicker (forwards and backwards) than when it's live. Of course the off line program is the one I use the most as it allows me to analyse the market more closely.

When I do the CellPainting on only the cells where the data has changed, performance is fantastic. As I said in a previous post, the CellPainting Event still wants to paint cells that don't need painting. The dilemma is, how do we stop the painting when we don't need it, and paint it when we do (resizing, scrolling, etc..)!

As an example, if I am running my app in a window, and most of the cells don't need updating, then I maximize the app, those cells that don't need updating will be messed up by whatever was on the screen at the time it was maximized. Also when I start the app, I have to make sure all the grids are drawn completely all cells intact.

It shouldn't be this hard to figure out!

Ok, I better leave it for now, it's 4:45 in the morning, time to hit the hay!!!
john1166  Thursday, May 14, 2009 6:51 PM
Ok, well things are becoming a little clearer about CellPainting in the DataGridView!

I have just been experimenting a little with the DGV and found out that the DGV makes some assumptions when cells are modified in some way. I used a simple integer counter inside the CellPainting event that just incremented every time the method was called. The counter is reset before any cell painting occurs. Only single cell selection was possible.

I created a DGV with 5 columns and 40 rows that were all visible on the screen. If the very 1st cell in the 1st row was selected and I then selected the cell in the row directly below, then only those 2 cells are repainted (obviously, unhighlighted 1 cell and highlighted the other). If I go back to the very 1st cell and then selected the cell in the 2nd column in the row below, then 4 cells are repainted. If I select again the very 1st cell and then the bottom cell in the 1st column, then the whole column is only updated (40 cells). And if I go back to the very 1st cell and then select the cell in the last row and last column, then ALL THE CELLS in the entire grid are repainted. Is that inefficient or what?

In other words selecting:
----------------------------
FROM TO REPAINTED CELLS
dgv.Rows[0].Cells[0] dgv.Rows[1].Cells[0] 2
dgv.Rows[0].Cells[0] dgv.Rows[0].Cells[4] 5
dgv.Rows[0].Cells[0] dgv.Rows[1].Cells[4] 10
dgv.Rows[0].Cells[0] dgv.Rows[39].Cells[0] 40
dgv.Rows[0].Cells[0] dgv.Rows[39].Cells[4] 200

The same thing happens when I selected a cell lower down then select one higher up the grid. If I minimize then maximize or resize the app, obviously all cells are repainted.

Now if there was a way to modify this behavior (could be something we've overlooked), a significant performance gain is definitely possible!
john1166  Saturday, May 16, 2009 8:11 AM
John,

That is very interesting. In my case, cells get repainted even without me interacting with the DGV -- in other words, my hands don't touch the mouse or the keyboard and cells get repainted even when their values aren't changing. Other cells values are changing, but the cells who remain constant shouldn't be repainted and they are.

Even though mine doesn't require any interaction, I have a feeling they are the same ultimate issue.

Would you mind starting a new thread with your last post, because it is a good, well-documented side effect you're seeing and I fear this thread is becoming stale. It might be good to have a new set of eyes read your post.
slikrik  Monday, May 18, 2009 4:56 PM
Ok Slikrik, or is it just Rik? I was beginning to wonder what happened to you!

Anyway, before we go and start another thread, about my last post, I'm not exactly sure if the entire cells are repainted! I will elaborate a bit more soon.

What I did after that post was do a little bit more Googling (searched for "DataGridView slow", using quotes) and found that the problem with the DGVs performance in part is indeed changing the BackColor of the cell (probably not so much as doing the painting but changing the BrushColor etc...). To test this, I made some changes to my own app so that cell BackColor remains constant and "VIOLA". Performance improved ten fold! When as little as 10 to 20 cells changing BackColor occurs, performance is severely degraded. Unfortunately, losing the BackColor of the cell, made my own app lose some of its appeal (color coded cells are easier to spot than black text). I could color code the text, but this is not visually as pleasing as a BackColor coded cell! Apparently the previous version of the DataGridView (the DataGrid) had better performance when it came to cell formatting.

Alright when I said I wasn't sure if all the cells are repainted in the test I performed in my last post, I couldn't see any flicker in the cells when I quickly moved the highlighted cell around the grid. In one particular case when the highlight was moved from top-left to bottom-right, the counter registered 200 (the count of cells on the entire grid). At least I am sure that's how many times the CellPainting handler was invoked! But not sure if all cells were repainted. Although it does make sense to repaint, because when a cells text changes, the background is usually repainted, and the new text painted over that so as not to leave remnants from previous text values. But it does seem very inefficient to invoke the CellPainting handler for all the cells on the grid when only 2 cells are touched!
I'm pretty sure your problem with the DGV is as I've outlined in my last post. Once you know what's going on inside the DGVs mechanism, things are more clearer.
Just a few more examples before I finish this post.

1. This is the easiest to understand - If only 2 cells have changed, like coordinate pairs, all cells within those coordinates are updated.
2. Random cells across the grid probably means most if not all the cells are updated.

I did one more thing with my test application,
I did a dataGridView.Refresh() * 100 loop. Consisting of 210 cells with text & same BackColor, to see how long it would take and the results are:
1. Inside the debugger - 8 seconds, about 12 a second with a lot of flicker!
2. Outside the debugger - 3 seconds, much better!
3. Applying 7 different BackColors to the 7 columns made no difference (hmmm), some further investigation is still necessary.

As for starting another thread, I'm not too sure if that will help! I've read threads on other forums that date back to late 2005 and MS were made aware of the DGVs performance in certain situations (large amounts of data changing very quickly on screen), and stated that there were no plans to fix it in future releases.

Incidentally, I am using C# Express 2005, what are you using?
Anyway, I am still certain that there is a workaround for this problem though! Sorry for the long post!!


john1166  Tuesday, May 19, 2009 5:50 PM
John,

It's actually Rick :) Yes, I guess I assumed in my earlier post that you were not explicitly changing the cell's foreground OR background color, as this severely degrades performance. You can still achieve background formatting, you just have to do it in your CellPainting method manually. By this I mean:

1) fill (paint) a rectangle the dimensions of the cell in the color(s) you wish
2) draw the contents of the cell overtop of this rectangle.

The DGV never knows of a change to the cell's background color. It always thinks the BackColor property is constant. When the cell is actually rendered, you override whatever the default background color is and paint a background of your choosing. This achieves huge performance gains over using the foreground and background color properties explicitly.

I'm using C# .NET 3.5, Visual Studio 2008 Standard.

Rick

P.S. Sorry for the delay - I was on vacation for a week.
slikrik  Tuesday, May 26, 2009 2:17 PM
Rick,

Well I hope you enjoyed your vacation!
Ok, I do agree with you that handling the cell painting yourself is more efficient than changing the foreground/background properties of the DGV control itself. My own app has
a tab control with 5 tabs and each tab has at least 1 DGV on it. Some of my DGVs require complete control over the cellpainting method due to the way I need to format each cell.
On these DGVs, each cell has 2 lines of text (different data text) in different fonts (bold/regular) and different brush colors as well. The text to be painted does not reside in the cell.value of the cell.

It's not possible to format the cells with this complexity unless you handle the cellpainting yourself. Unfortunately, some of my cellpainting routines are over 300 lines in length (ouch) as different columns require different formatting! That's a lot of code to get through for just 1 cell! On the tab pages that have only 1 DGV on it, performance is fine. But the tab page that has up to 6 DGVs, as I have mentioned above, the performance is not adequate for my liking. These DGVs are not as complex as my other ones, however they contain a lot more cells and I have not resorted to handling the cellpainting fully as yet. I have only tried a few ideas, they're fast but leave a mess behind.

On a small grid with only a few cells it's ok to update all cells (invalidate the whole grid), you won't really have any performance issues. But as soon as the number of cells start to rise, the performance drops!

In my case there is however one problem that I can see with handling the cellpainting event, unless of course all cells are updated whether they need it or not.
By this I mean do the following:

1) Enter the CellPainting routine
2) Determine if new data is different to old data
3) If so, process cell accordingly or leave cell as it is
4) e.handled = true; and your done!

To start off with, the grid most likely will not have drawn correctly when the app was started, unless somewhere in the CellPainting routine you force it to draw completely.
If your grid is fully intact and you've got data coming in and the cells are being updated accordingly, then try this! Use the calculator (or any other window) and drag it across part of the grid where the data is unchanging. You will see the trail it leaves behind in those cells where the data was static. Obviously this is because of step 3 above when the cell(s) in question were bypassed in the CellPainting routine. Resizing your app or maximizing will have the same effect.

There must be a way to update the whole grid only when it's necessary to do so and only update the cells that require it all other times!

John.....
john1166  Friday, May 29, 2009 6:37 PM
Hello, anybody out there?

Well it's been about three and a half months since I last posted and I gave up on the DGV for a while!

But now it's come back to haunt me again!!!. I spent the last few days streamlining some code and using the CellPainting event exclusively to format the cell after
a call to InvalidateCell. I did not use the dgv.Rows[row].Cells[column].Value to modify the contents of the cell. Rather, I used the cell's Tag to store and retrieve
data that was manipulated. When only a few cells values were updating performance seemed about twice as fast. However as more cells change value, performance
had dropped considerably! So then I decided to use counters inside specific events and discovered that cells that were not invalidated were still getting repainted!
The events were still being triggered. What's going on?

I know the DGV is capable of better performance and lord knows I've been trying to get under the hood and find out how it ticks. I didn't realize quite a few events
are triggered each time a cell gets updated (painted). Below is a short list of the most common ones:

  • dgv.Paint()
  • dgv.CellPainting()
  • dgv.CellFormatting()
  • dgv.RowPrePaint()
  • dgv.RowPostPaint()

Without implementing any of my own code in these events except for an integer counter, I created a simple DGV of 2 columns with 5 rows (10 cells in all).
I used 2 different techniques to change/invalidatecell 2 cells only (indexed at 0, 0 and 1, 4). The 1st and last cell in this small grid. I had 2 buttons on the
Form, 1 button to clear the counters and the other to trigger the events.

  • 1st Technique: Change the actual value of the cell: dgv.Rows[row].Cells[column].Value = "some text";
  • 2nd Technique: Invalidate the cell to force a repaint: dgv.InvalidateCell(column, row);

I had a hunch that both techniques would produce the same result. And I was right! Both the RowPrePaint() and RowPostPaint() were triggered 5 times (all 5 rows)
Both CellFormatting() and CellPainting() were triggered 10 times (once for each of the 10 cells). No other interaction was made with the DGV. All I want to know is
why are all these cells being repainted when only 2 cells were modified? Is there a way to turn this unwanted behavior off? Is there something I'm missing here?
I'm pulling my hair out here and God knows I don't have much left! Starting to look like Homer Simpson!!!

"Rick", the person who started this post, are you still out there? Did you solve the problem? Does anybody know??? aaaahhhhhh!!!

Help much appreciated

John......

john1166  Tuesday, September 15, 2009 9:10 PM

You can use google to search for other answers

Custom Search

More Threads

• Updating a databinding
• How can I avoid having my form trashed by making changes to a table adapter ?
• checkbox being unchecked not updating rowstate
• setting DataGrid scroll value
• Tip: Use the power of DataBinding, and avoid all the DataSet and SQL silliness.
• How to implement transaction?
• Sample Code: DataGridView progress bar column
• Cross-Thread/Datagridview Problems
• WebService Error
• Databindings and related datatables