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.
Related
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.
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.
I am using VS express 2010 and .Net 4.0.
I bind a DataGridView to a BindingList which implement INotifyPropertyChanged.
The value changed is reflected on the grid normally but failed when using Threading.Timers to do the update job.
As shown below , the value is updated but not shown unless the cells lost focus like "minimizing and maximizing" or "the grid is covered by other window" or "selecting the cells".
http://i.minus.com/jViIpKKeNNRrB.PNG
I am a newbie. I had searched and I believe the threading.timer is bad in updating UI. But in my case, I am just updating the value.
I am tired of playing with the weak threading.timer. Any other recommendations for light-weighted timer would be appreciated! Thank you.
The Threading.Timer isn't "bad" at updating the UI, it just fires its event on a different thread to the UI thread and since UI must be updated on the UI thread you're having your difficulties.
You need to Invoke the update back on the UI thread.
The easiest way to do this is:
myControl.Invoke((Action)(() =>
{
myControl.Text = "Hello";
}));
Obviously replace your code into my example.
Let me know if this works.
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
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.