I have a form, "StartForm" where the user makes a combo box choice, and then clicks a button. From there I would like to:
Generate an instance of another form, "MainForm"
Populate the DataGridView on that form with around 50 000 rows
Loop through each row in the datagrid view and perform some processing on it. In my case, it's formatting the row colour based on the value of a cell, and updating some columns.
Display the form.
This is straight forward, however because of the large number of rows and the processing time on each one, the UI freezes when constructing the MainForm. So I need the MainForm to be constructed on a background thread, and a progress bar to fill up on the StartForm while this is going on.
I keep getting cross thread exceptions or my formatting is lost when using backgroundWorker, and control.Invoke() also seems to throw exceptions. Maybe I'm just not using these correctly...
Could someone please explain how to accomplish the above? I have tried to keep the question basic to understand so it can be helpful to others but if you would like my code then please ask.
Thanks!
I think you need to implement virtual mode for your data grid. Take a look on msdn article about implementing Virtual Mode in the DataGridView Control. Small sample:
grid.VirtualMode = true; // enable virtual mode
grid.RowCount = source.Count; // 50000
grid.CellValueNeeded += grid_CellValueNeeded;
grid.CellPainting += grid_CellPainting;
Use CellValueNeeded event handler to provide value for cell (i.e. select value from source). Use CellPainting event handler to set cell color based on cell value (use e.CellStyle property). If it takes long time to fill your source with data, you can do that in BackgroundWorker. But do formatting and displaying data in virtual mode - this is the best option, when you have huge amount of rows (btw consider apply some filtering - rarely users need 50000 rows of data at once).
By default, all the work is done on the UI thread which gives the illusion of 'crashing' since the UI becomes inaccessible during the heavy work. Creating a form itself on a thread that's not the UI thread is possible but would mean that the form is only ever accessible by the thread that created it, so it's unlikely that's what you want. You're getting the cross-thread exception because you're trying to modify the UI thread from another thread.
What you could try is to load and process all the data on a background thread, but load it into a new, temporary DataTable. Then, use DataSet.Merge on the UI thread (possibly in the RunWorkerCompleted event if you're using a BackgroundWorker) and merge the temporary DataTable with your main DataSet which is bound to the DataGridView.
That should mean all the processing and heavy work is done on a background thread, but the DataGridView control is updated on the UI thread, thus bypassing any invalid cross-threading. One side note is that you should test the performance of the DataSet.Merge.
Related
On a winform application,I've the following scenario:
A socket receives data with high frequency (150-200packets x second )
I've a grid view that is populated on the decoded packet received
the received item can be a new item or an update of a previous one..there're 5possible value that can be updated
the gridview is in virtualization mode : on
Since I can't bind to a BindingList (it will freeze the Ui thread) I've decided to use a Queue and to update the grid view based on a Timer. When the timer's elapsed event is processed I update the gridview calling the
gridview.TemplateMaster.Refresh() causing the cellValueNeeded to be called
This is the fastest solution I've found but I have some problems:
I can't know which column has been updated
Using the virtualization on cause me to call the cellValueNeeded each time and my List is almost useless, I can use it as a bi dimensional array
I've to custom implement sort/filter/column reorder
Anyone of you has got a better approach ?
Thanks
I would also recommend having a look at this article: High Performance with RadGridView and Virtual Mode including Filtering, Sorting and Grouping
Given the following additional information:
I've got 8 columns... the row number depends of which item is dragged
from a treeview... in this test case I've got 256 rows but can be more
...for the second point If I rebing the whole itemsource I loose what
has been updated
your issue is much more complex than your OP suggests. You actually have two sources of changes that modify the grid and the underlying data. One is from your incoming data packets and the other is the user. You also have 8*256=2048 items to show the user. That is a lot. And from my understanding of th OP the UI can potentially be updated with as much as 1,000 new values every second. This is not a useful feature since no human being can process information that quickly.
So, given the fact that the user can arbitrarily pull in data into the grid from a tree view and your packet receiver could add or update a thousand cells of grid data per second I would suggest a very different approach:
Keep the queue collection that holds incoming data
Add three new pieces of UI just above the grid:
A read-only control (simple label control would do) that displays the count of updates currently in the queue
A time-stamp of when the grid was last updated from the data packet queue
A button which the user would hit manually to pull in the queued data. Clicking the button would reset the time-stamp to the current time and the count of queued items to 0
You could also keep your timer to automatically update the data but make it smarter:
Extend the time period between automatic updates
Reset the timer whenever the user interacts with the UI at all (this would basically create a situation where the grid is automatically updated only when the user is "idle", or not interacting with the UI)
The columns being moved around is not a problem since you can find/and refer to columns by their column name rather than their index
Since the grid can be sorted arbitrarily by the user you need to use a find function to identify the row to update. You should have some sort of (primary/unique) key column, visible or hidden, by which to identify a row and the underlying object
All other functionality stays the same
Original answer for posterity:
I suggest creating a new collection which would be a shared resource between the UI and your socket thread (assuming you have a thread for the socket communication). You would update this collection as well as a flag time-stamp of the latest change. This flag is important because you would not change it unless data did actually change. You could also have an additional field which could count the number of updates since the latest change. (I'm assuming/hoping that much of this data coming in would not actually be different.)
So now you have an internal cache that can be shared by both threads. You don't have to worry about synchronization issues because one thread only writes and the other only reads.
I was working on a c# project using a DataGridView and ran into an issue.
Basically, I have a grid that gets updated from any number of places and has a button column.
I want to capture button clicks (of course) and do something which involves the other cells from that button's row. As far as I know, the only way to associate a click with a row of the grid is via the RowIndex of the EventArgs.
What I'm worried about is that the grid may change between the user clicking and the event being delivered, causing an incorrect row to look like it was clicked.
Can this happen or am I being paranoid? If possible, is there anyway I can bind a reference to either the button or the row inside the eventargs so that I can differentiate the originating row even if its index has since changed?
GUI controls in WinForms are 'tied' to the UI thread.
When you click the row, grid implementation handles this and as a part of the click handling raises an event - all on the same UI thread. Even if other threads change the grid content, this has to be serialized to the UI thread (typically with posting Windows messages in the background, or BeginInvoke in WinForms terminology - not exactly the same, but close).
This means that even if there are concurrent changes, if your click is already registered, the event will not be interrupted by UI update.
Note, however, that background thread could change the data object to which the row is bound while your handler runs, or between click occurred and event was handled. Still, it makes no difference to you, as grid UI update would need to be serialized and would still happen after your event handler.
For a project I am working on I have a form with a bunch of DataGridView components that are to display some data. Every DataGridView has it's own DataTable associated it. The data that is to be displayed is send periodically. My application has to read this data, parse it and fill the datagrids accordingly. Because I want to maintain responsiveness of the form I implemented the receiving of data (blocking) in an endless background worker.
In the background worker I obtain the data and parse/convert it into values that fit in the DataTables. Now here is my question: At the moment I assign these values directly to the DataTable objects. (So I do this from within the backgroundworker's DoWork event)
I am wondering if this is valid. I did have an index out of bounds exception once and I was wondering if this was somehow associated with this. Is this a safe and recommended way of doing it, or should I use invokes in my backgroundworker's DoWork event to update the DataTables?
No, all properties on .NET WinForm controls (this is the assumption) that affect the rendering of the control (including values bound to the control which would affect the rendering) must be made on the thread that created the control.
That said, there are many times when you will get away with being able to make the changes, but the behavior is unpredictable and not recommended.
In your specific case, I'd suggest having a copy of the DataTable that the processing thread works with, and then marshal that copy to the UI thread (through a call to one of the ISynchronizeInvoke interface implementation, which the Control class implements) and update the grid in the UI thread.
Basically, you'd perform an update of the DataTable that the grid is bound to with the copy marshaled from the background thread.
It's fair to say that you should never update any UI bound elements from a non-ui thread.
Although very often you may not see any exception if you do so, it never good practice, and often leads to exceptions or worse still, unseen errors
Well, no.
Though it is a very good idea to do calculation in a background thread, UI update should be done in UI thread, always.
When binding Datatable to UI element, you "give" ownership on these objects to the UI thread and should no longer update them in background threads
I have a DataGridView which I populate with values as soon as they are returned from an Udp-server. When there are only a few values my program works, but if enough values are retrieved that the dialogbox needs to expand outside the dialogbox, the DataGridView freezes.
I've created this simple method for populating the DataGrid.
public void populateGrid(string[] data)
{
dataGrid.Rows.Add(data);
}
The Winforms DataGridView should indeed be able to expand easily beyond what's visible. One of the programs I wrote routinely has thousands of items in some of its DGVs, and DGVs are used everywhere in that software. I populate the lists using the Rows.Add() method, not unlike what you're doing.
The question becomes, how are you using this populateGrid method? Are you looping through calls, or are you using controls with user-entered data to form the array for each row? How is the GridView set up (readonly? Can the user add/delete rows?) Are there events to respond to resizing of DGV elements? Are you "lazy-loading" the records from some data source to populate this DGV?
Try attaching the debugger and hitting the "pause" button to see where the system is hanging. You may see repetitions in the call stack indicating an infinite loop of event handlers, or it may be hanging on a method call you expected to be fast but is waiting for a troublesome data source or pulling MUCH more info than you need.
I'm not exactly sure how or why, but by creating a backgroundworker and invoking a delegate on the DGV I accomplished adding items without the system freezing, and the backgroundworker reaches it's Completed event.
I have a DataGridView with about 400 rows and 10 columns. When the user first displays this table, it receives all of the data from the server and populates the table. The DGV uses a DataTable as it's data source, and when updating the DataTable I use row.BeginEdit/EndEdit and acceptChanges, but when the View itself is updated it lags for a second while all of the DGV is being updated. I am wondering if there is a way to make this smooth, so that for example, if the user is scrolling through the data and it updates, it won't interrupt the scrolling. Or if the user is moving the display around the screen and it updates, it won't interrupt. Is there an easy way to do this? If not, is there away to prevent the DGV from updating the view until all events have ended so it won't be repainted until the user stops scrolling, dragging, etc ?
In the past I've found that slowness issues can be related to the auto-sizing properties. Here are some more ideas as well: Quick fixes for slow DataGridView
The first idea that comes to my mind is to use a BackgroundWorker. Then, while your BackgroundWorker updates, it doesn't occupy the main thread, that is, your GUI thread.
Try running debugger/profiler to determine which part is the actual cause for slow down. Once you figure out which line(s) are the culprit it will be easier to answer this question.