Grid Exception
Hi,
One of my applications is throwing an exception at random and I'm trying to figure out why its happening. This is the exception text
************** Exception Text **************
System.InvalidOperationException: Object is currently in use elsewhere.
at System.Drawing.Image.get_Height()
at System.Drawing.Image.get_Size()
at Dapfor.Net.Ui.GDIPlusRender.DrawImage(Image image, ImageSettings settings, Rectangle bounds, Graphics graphics)
at Dapfor.Net.Ui.PaintRowEventArgs.PaintRowSelectorImages()
at Dapfor.Net.Ui.PaintRowEventArgs.PaintAll()
at Dapfor.Net.Ui.Grid.Impl.PaintRow(Row row, Rectangle rowBounds, Rectangle clipBounds, Graphics graphics)
at Dapfor.Net.Ui.Grid.Impl.PaintBounds(Rectangle clipBounds, Graphics graphics)
at Dapfor.Net.Ui.PaintRectangleEventArgs.PaintAll()
at Dapfor.Net.Ui.Grid.OnPaint(PaintEventArgs e)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at Dapfor.Net.Ui.Grid.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
I would like to know if this exception is being raised when an image is being set in the cell or do these methods get called even for normal text rendering. I do set images in certain columns in the paint_cell override. I set it in the PaintCell(object sender, PaintCellEventArgs e) method by setting e.Image = <filename> but I'm not sure if this exception is being raised by an image file being used by multiple threads or something else. Your inputs would be much appreciated.
Thanks.
-
Dear Deepak,
This exception happens while painting an image (may be the focus arrow) on the row selector button. It seems that the image’s width or height is 0. Normally the grid should verify image's size and we’ll investigate the problem on our side. Do you use themes? Do you use binding list with possibility to add new rows?
Temporary you can desactivate row selector image painting to prevent from this exception:
grid.PaintRow += delegate(object sender, PaintRowEventArgs e)
{
e.Parts ^= e.Parts & PaintPart.RowSelectorImages;
};Regards,
Dapfor
0 -
I do not use themes. I use ThreadSafeBindingList as the data source. The problem doesn't happen when adding rows. The grid is really static in terms of row addition/removal. It seems to happen at random at some paint event. The only difference with this grid is I'm using two grids on two separate gui threads. This was done to improve performance. I will try to deactivate the row selector image and let you know what the result is.
Thanks.
0 -
We think this post will be interesting for you: http://support.dapfor.com/entries/21324543-Message-pump Could you say if your application with multiple message looops is more productive?
On our side we'll investigate the problem of InvalidOperationException with two message loops.
Regards,
Dapfor
0 -
Yes, I did go through that post in the past. The problem we are seeing with our application is that when our application is hit with a high number of updates, one grid seems to take up all the drawing while the other grid is almost frozen. We ran a test in which we had both grids up with around 50 rows each. My application then started receiving sequential updates at a very high rate. We noticed that one grid would keep updating but the other grid would not. The second I clicked on the second grid, it would show the latest data update and then continue updating but the first one would remain frozen. It would remain frozen until I clicked on it again. I agree this was an extreme test case, but we did see an improvement in a real time scenario when using multiple message pumps. The grids used an object that implements INotifyPropertyChanged and the objects on both grids were updated on a single non gui thread. Please let me know if you have any thoughts on this. Thanks.
0 -
The Windows dynamically changes thread priority for active/inactive windows in different processes. In other words a process with an active window has higher priority against other processes. We think the Windows uses the same approach for applications with two and more message pumps.
Moreover GDI is a single-thread system inside the process even you use multiple message pumps. To ensure thread-safety the Windows uses locks to prevent from thread collisions incasing thread contentions. So, our recommendation:
- The application should have a single message pump.
- The application shouldn’t perform expensive operations (loops, complex computing) in the GUI thread.
Regards,
Dapfor
0 -
I agree having multiple message pumps is not always the best choice, it was not my first choice, but it seems like that is the only choice I have right now. I tried spying on the windows messages on each grid. I used a SpyEx (http://thesz.diecru.eu/content/spyex.php) to do that. It seems like when I have the grids on one message pump, one of the grids never gets the wm_paint message, but the other seems to be inundated with the dapfor gui dispatcher message and it also seems to get the wm_Paint message every so often. But when I use separate message pumps, both the grids get wm_paint messages at regular intervals. I am not doing anything on the gui thread, only updating my objects on a non gui thread. In my extreme test case, the grid used to get updated every 10 millseconds and the rows had 4 columns each with 50 rows on the grid. So every 10ms, all 50 rows would get updated with 4 values each. Also, all 50 rows on each grid were visible. I was definitely able to notice a difference with how grids updated in this test case. If its possible can you test this scenario with 2 grids and single/separate message pumps and let me know what your thoughts are. Thanks.
0 -
Dear Deepak,
We wrote a simple application that demonstrates problems encountered when multiple message pumps are used. Indeed in some cases the second grid doesn't get WM_PAINT messages and we can't explain why. It seems the problem is in message pumps working in different threads.
Please find attached sources of the application. Note, it refreshes 2 grids of 50 rows & 4 columns each 1 msec without any problems.
Kind regards,
Dapfor
TestApp2.zip0 -
Thanks for the update, the results are interesting. Let me take a closer look at your app and mine and get back to you.
0 -
I made some changes to your test application to mirror my application requirements. I have attached to modified project. I am using separate lists for each grid. To me it looks like both architectures perform equally. I do see an issue though, I have added a line to print out the number of updates. The update rate should really be a 1000/second. It seems to update at that rate when the grids are not open, but the second I open them the rate drops drastically. Do you know what the reason for this drop is ?
Thanks,
Deepak
DapforTest.rar0 -
Yes there is the reason:
Update rate is not 1000/sec but 400 000!
Indeed, you have 2 collections of 50 elements each. Each 10 msec the DataObjects.RandomUpdate method is called 10 times. It iterates all 50 objects in each collection and calls DataObject.RandomUpdate() of each. Finally 4 notifications are fired at each call.
So, theoretically we have 100 (upd/sec) * 10 (times) * 50 (elements in each collection) * 4 (INotifyPropertyChanged) * (2 collections) = 400 000 updates /sec.In reality we have about 150 000 updates per second with cell blinking what is not too bad for the visible area of the grid!
Our recommendation: don't use multiple message pumps. We can not guarantee stable behavior of the application with multiple message pumps at high CPU load.
Best regards,
Dapfor
0 -
Dear Deepak,
We've released a new version of the grid with bug fixes.
Best regards,
Dapfor
0
Please sign in to leave a comment.
Comments
11 comments