Is it safe to update a databound DataTable from a thread? - c#

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

Related

How to detect when a ListView or any control is finished rendering?

I am working on a Uno project using both UWP And WASM. The project contains a ListView or GridView with a source that is an ObservableCollection. Since it is getting data from an API. I am displaying a progress indicator, what I am finding is that the progress indicator stays active until the ObservableCollection is populated with data items.
The issue is that there is a lag between when the ObservableCollection has the data and when the control is finished rendering the view. In this case, it is an ObservableCollection of images and the GridView or ListView does not populate.
I have not been able to identify and event that will tell me when to inactivate the progress indicator.
There's no event defined in the WinUI API that tells you when the UI has completed rendering, so you have to create your own. This can be worthwhile when you have a complex, heavy UI that takes a visible delay to render after it's obtained data.
There are a few possible strategies:
hide the progress indicator after a fixed delay.
Schedule a callback using CoreDispatcher.RunIdleAsync(). This should only be raised when the UI thread is not 'busy', which is a rough proxy for it having completed rendering. Eg: Dispatcher.RunIdleAsync(_ => progressRing.IsActive = false);
Specific to ListView/ItemsControl: You could check that the list actually has item views with the ContainerFromIndex() method, eg listView.ContainerFromIndex(expectedIndex) != null. Be careful as the list only creates containers for items that are actually in view.
Specific to Image: Images sourced from remote urls are loaded asynchronously. You can subscribe to the ImageOpened event to get a callback when the image is ready.
You'll probably need to experiment to find what works best in your scenario; this is not an exact science. If possible, it's a good idea to test across multiple devices with a range of performances.

Why is WPD Datagrid Usercontrol bound to row datacontext not updating?

I have a DataGrid bound via a ListCollectionView to an ObservableCollection of Objects with type Job, say. Each cell in the DataGrid contains a UserControl which has a dependency property named Job which is bound to the DataGridRow.DataContext (using Mode=TwoWay). Everything displays correctly.
The problem is that I have a background process which mutates objects referenced by the Job object and those get displayed by the UserControl. Obviously, Job does not change so the view does not change.
How can I get the user controls in each cell to update themselves with the new data?
With lists there are 3 kinds of ChangeNotification you have to take care of:
the one for each property of the Job Class.
the one if elements are added or removed from the collection. That is the only part ObservableCollection takes care off.
the one on the property exposing the list/CollectionView/whatever. ObservableCollection lacks a AddRange function so on large operations (like a rebuild of the list) it would spam the UI with changes. The solution is to build a new list in the background, then expose it in this property.
One particular issue might be the Background Process too, if it is Multithreading. GUI's are protected against being written from the wrong Thread. But threads are also notoriously good at swallowing Exceptions. Usually you need to write a faulty Catch, but they do it for free. As a result, your might run int oa CrossThread exception and never notice it.
For a better answer, we need some actuall code. Like teh job class, the BackgroundProcess, etc.

Background worker is updating model in UI thread without issue

I'm using a BackgroundWorker and ive implemented ProgressChanged method which gives me e.UserState to pass to my UI, which works OK.
As an experiment I changed my DoWork Method to work on the same model that is bound to the UI ListView (which is an Observable Collection)
The UI updates when I do this, & im wondering why it manages to do it flawlessly after many tests?
Im going back to using the e.UserState object but im wondering if it is normal behaviour & whats the potential problems?
You can certainly change your model's properties from a background thread without any trouble; the binding engine will determine what changes need to be made to UI elements and invoke those on the dispatcher thread¹. This is normal and will not result in problems.
What you cannot (and never could) do is directly change properties of UI elements from the same background thread.
¹This has always been possible, and in fact it couldn't be any other way. Consider that some code (e.g. a model) needs to change a property on another model. This code surely cannot know that the property has been bound to any control, much less which control that is. Therefore it would not only be inconvenient but also horribly difficult to marshal the property change off to the appropriate UI thread each time.

Formatting winforms UI on a background thread

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.

silverlight 4 - fastest/simplest way of scheduling work on the UI thread?

UI tree:
listbox with the april 2010 toolkit's listboxdragdroptarget
listbox item template includes a control that has a a couple of buttons
the click handler in question is in one of those buttons (and therefore part of the actual listboxitem in the UI, so a potential drag-drop operation)
the overall listbox item should be able to drag (to rearrange within the listbox, or move to another listbox), but the goal is to keep the click handlers on these buttons from triggering a drag
Currently the click handler on one of the buttons (see above) appears to take long enough (it does a bunch of updates to the viewmodel, which cause various other UI changes, so it needs to be on the UI thread AFAICT) that it very often causes the drag event to start.
The first thought on getting this code out of the click handler is to create a BackgroundWorker with no DoWork and put it all in the RunWorkerCompleted. However, that feels like both an abuse of BackgroundWorker and kind of heavyweight. The effect I want is akin to just PostThreadMessage on the same thread (the UI thread) but I'm not seeing anything jump out at me for how to do so quickly.
I could certainly queue up something with the threadpool or even a new thread and then have it marshal it back over to the UI thread, but again that seems like quite the abuse.
I think Dispatcher.BeginInvoke with a low DispatcherPriority behaves almost like PostThreadMessage.

Categories

Resources