Modifying ObservableCollection while it is being displayed - wpf

I have a tree view in which the ItemsSource is an ObservableCollection. I am dynamically loading the children contents every time the user expands the subtree by using a background worker and modifying the underlying ObservableCollection that corresponds to the children nodes. However, when I try to modify this ObservableCollection inside that thread, it will fail IF AND ONLY IF the tree is already expanded (meaning that if the number of children is small, then it will correctly populate).
What I am hoping to achieve is once I expand the tree, I can see children being dynamically populated as they get processed. How can I achieve that behavior using ObservableCollection?

How can I achieve that behavior using ObservableCollection?
The problem is that you aren't allowed to update a collection on a background thread in WPF. The binding system will automatically marshal most simple bindings to the UI thread, but not collections. You have two options.
You can marshal the call to add the data back onto the UI thread using Dispatcher.Invoke or Dispatcher.BeginInvoke. This will allow the data to be added, but not cause a cross thread exception when WPF updates the binding.
(If you're using .NET 4.5) You can use the new EnableCollectionSynchronization on the binding to allow cross-thread access to the collection.

Related

WPF DataBinding Thread Safety

I have a TextBox that's bound to a property that gets modified at a very rapid rate in a background thread. Is data binding in WPF thread safe? Will the data in the property or the TextBox ever get out of sync? Is it necessary (or even possible) to use synchronization on a property that takes part in data binding?
I understand that, if the class on which the property resides implements INotifyPropertyChanged, the binding framework automatically marshalls the UI update to the UI thread. However, doesn't that still allow the data to get out of sync? If I understand correctly, variables that are written from one thread and read from another should by synchronized... is data binding an exception?
thanks!!
Yes, for the most part. Binding is thread safe for single objects (so should be fine for your string). However, binding to a collection is not thread safe - and still requires manually marshaling. If you have a control bound to a collection, you cannot change the collection on a background thread.
I understand that, if the class on which the property resides implements INotifyPropertyChanged, the binding framework automatically marshalls the UI update to the UI thread. However, doesn't that still allow the data to get out of sync?
This shouldn't get out of sync, unless multiple threads are writing to the variable very quickly (in which case, they'll all block until they get back in sync, but there is a period of time where threads will "wait" on the UI). The marshaling happens synchronously, so the thread doesn't receive values until the binding is up to date. This can slow down your processing, as the UI update has to happen before your background thread can continue.
Yes, it is usually thread safe. In WPF (unlike WinForms) the data binding classes look for the UI thread's Dispatcher and use it (if needed) to automatically marshal to the UI thread. Note, however, that this is done synchronously- your background thread will block while the UI is redrawn, and I have seen that cause choppyness, "freezing", and other unintended effects with rapid background updates.
See a similar question here: WPF Databinding thread safety?

WPF MVVM updating the View Model on a Background Worker

Is this senerio valid?
I have a View to maintain an Item.
I have View Model which exposes the Item Object (implements INotifyPropertyChanged) as a Property to which is View is bound.
Is it valid for me to pass the Item object to a Backgroundworker where it is modified, then raise the PropertyChanged event when the BackgroundWorking is complete?
Or should the BackgroundWorker in no way modify the Item object. I would update the existing Item object with the results passed back by the BackgroundWorker. This would be done in the RunWorkerCompleted event. But does this lock the UI thread and defeat the object of having a backgound worker?
Confused?
I'll try to explain.
The User takes an option to create an Item. I create the View and View Model. In the View model an empty Item object is created. He is presented with a View to maintain the Item. On selecting the Item Type property, this instigates a complex process to create a list of proerties to be entered by the User. I could block the UI thread whilst the list is created but this gives a poor user experience. I want to pass the processing to a background thread while keeping the UI alive. At present, I set a flag to indicate the section on the View is loading, pass the Item object to the BackgroundWorker which updates the observable collection of Properties. When the BackgroundWorking is complete I call the PropertyChanged event which updates the section of the View which is bound to the list and turn off the flag to indicate the section is loading. This seems to work with no issues. But I have a gut feeling that I should not be updating bound onjects from the View Model in a background thread.
Thanks Tim
This sounds ok. As long as your item-object is no DependencyObject, you can change the properties of them in the background worker.
DataBinding to properties of your object will work, the binding engine will do the thread-switching automatically for you.
However, dont't fill data-bound collections or manipulate properties of DependencyObjects (such as UI-Controls) in the background worker without dispatching the manipulations. This would lead to an exception.
Edit:
Only for clarification: The real question is not, if the item-object is a DependencyObject but if the property is a CLR-property or a DependencyProperty. Because DependencyProperties are bound to DependencyObjects, I often use the above simplification, but its not the full truth.
This means that if you have a CLR-property, you can set its value from a foreign thread, regardless of your class is a DepenendencyObject or not. This is a slight difference to my first statement.

INotifyPropertyChanged, ObservableCollection, Threads and MVVM

Ok, following on from yesterday I've added a new layer of complexity. We still have a theoretical Model class, ViewModel and View. This time my Model has a Threading.Timer (Chosen specifically to get timer callbacks on the "wrong" thread.
The Model has an ObservableCollection. The Timer callback adds items to the collection.
The ViewModel simply passes the collection to the View which contains a listbox bound to the collection.
This doesn't work.
The model also exposes a string which is updated in the same timer callback.
This too is exposed via the viewmodel and bound to a TextBox.
This does work.
I've seen hints in my googling that updating collections doesn't make INotifyCollectionChanged work as expected. I get a total implosion, not even an exception, just immediate termination of the application.
So there are two questions:
One relates to our discussion yesterday. I'm using INotifyPropertyChanged and ObservableCollections in my Model because they are the thing upon which the view does work. It still makes sense to me to use these mechanisms to notify my viewmodel, or what ever that the underlying model has changed. So how do I deal with updates occuring on a different thread?
Second, what is happening that makes INotifyPropertyChanged work with the binding? I'm binding a string property to a DependencyProperty called Text, so is it the DependencyProperty system that marshals my change back to the UI thread? Edit: And can I rely on it, i.e. does it do this because they expect me to talk to it cross-thread, or is it just a catch all that I shouldn't rely on?
The ListBox is bound through ItemsSource="{Binding ObsCollection}". When this crashes the application. Actually, at first I started the timer when the Model was created which happened when the Window's DataContext was set, so it would actually bomb Visual Studio...
Thanks
WPF controls have thread-affinity, what this means is that their properties can only be modified from the UI thread. Therefore, if you update a property value from a Timer (other than a DispatcherTimer), you will have to marshal this update onto the UI thread. This is perfomed via the dispatcher:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => // update your control here));
The databinding framework does not ensure that updates are marshalled onto the UI thread, therefore, if you update your model from a different thread, this will cause issues. therefore you need to use the same pattern as above. In other words, if you are adding objections to your observable collection, this add must be performed via the Dispatcher.
This problem is quite prevalent in WPF. I think the best option is to have your own ObservableCollection<> subclass that takes care of dispatching event notifications to the UI thread automatically.
And since the wheel has already been invented, I 'm going to simply refer you to the answer to this question: ObservableCollection and threading.

How would I loop through a collection in a separate thread and reflect the current item on the bound ListBox?

I have a ListBox bound to an ObservableCollection. The items in the collection each signify a different step with a method to perform that step. What I would like to do is have a BackgroundWorker loop through the collection, run each item's action method, and have the current step be reflected by the ListBox.
My first iteration used no threading and was just to get the step running going. Now I have created a ListCollectionView, set it to represent the data in the ObservableCollection, and have bound the ListBox's ItemsSource to it instead of the ObservableCollection. I noticed running through the steps still blocks the UI thread's updates even though I'm explicitly incrementing the CurrentItem.
I want to use the ListCollectionView inside a BackgroundWorker but most examples I'm finding are written assuming you are modifying the contents or sorting of the list. I don't wish to do this; I simply want to increment the CurrentItem for each iteration. I'm guessing simply referencing it won't get me very far as it is tied to items on the UI thread and the compiler will complain. Any thoughts or pointers would be much appreciated.
You shouldn't be seeing any complaints from the compiler, but you will be getting exceptions at runtime if you try to update anything that fires an INotifyPropertyChanged PropertyChange from the background thread. There are a few ways around this. You can use Dispatcher.Current.Invoke to just do updates from inside your DoWork method. You can try to rig something through a ProgressChanged handler (runs on the calling thread) and calls to ReportProgress for each completed step. Or you could do the updates in the RunWorkerCompleted handler (also runs on calling thread) and maybe use a series of consecutive BackgroundWorker calls that are triggered from the previous one's completed handler (this can get messy if you don't manage the steps through a generic queue or something similar).
You need to check out WPF Cross-Thread Collection Binding – Part 4 – The Grand Solution and the source I think is on QuantumBitDesigns.Core
This allows you to update a list from another thread and have the changes automaticaly reflected on a bindable observable collection.
Figure: The demo app shows multiple updates to a single ObservableCollection
I have used this on multiple projects with fantastic results.

How can I override TreeViewItem in WPF to allow asynchronous children loading?

I am using MVVM as my WPF architecture and I have a WPF TreeView implemented as delay loading (child items are not loaded recursively until you expand).
However, I need to implement asynchronous loading as well upon expansion. Is there a way to do that? I need to keep this in control level and not in code-behind/application level.
Thanks
Why do you need to implement this in the control itself?
Assuming that isn't an absolute requirement, I would handle this in the property that returns the children of the specified node. If the children haven't been populate yet, use a background thread to load the children, and as they are found notify the UI thread and then add the child object to the collection of children. Assuming that you're using an ObservableCollection (or at least a collection that implements INotifyCollectionChanged), as the children are added they will appear in the UI asynchronously.
If you use lazy loading and HierarchicalDataTemplates you will only get the items you've selected loading. Keep in mind, the TreeView will need to load one level below what it is displaying to determine whether or not it should show the Expand/Collapse Toggle button
Here is how to do this:
Create an attached property of type IEnumerable
In the PropertyChanged handler for the property, cast sender to TreeViewItem and check the IsExpanded property.
If IsExpanded is true, do a Dispatcher.BeginInvoke at ApplicationPriority.ApplicationIdle priority to your fill routine
If IsExpanded is false, set an event to detect when it becomes true and in the callback do the Dispatcher.BeginInvoke to your fill routine
In your fill routine, set the ItemsSource of the target control to the attached property value
Use a style to set your attached property on TreeViewItem instead of setting it through HierarchicalDataTemplate (omit ItemsSource there)
This prevents the TreeViewItem from ever having its ItemsSource set until it is expanded. Creating this mechanism is some work, but once it is created you can make any TreeView delay-load by simply removing the ItemsSource from the HierarchicalDataTemplate and setting it in your ItemContainerStyle instead.
For every UI element that needs delayed loading I have used a 'View GUID'.
Explanation by example:
At the beginning, Control.Tag = "0000-0000 ...."
User makes an interaction,
a random GUID is generated, namely, guid1
Control.Tag = guid1
a thread is created which takes (what do to, control, guid1)
in between, the user makes another interaction
a random GUID is generated (guid2 this time)
Control.Tag = guid2
a thread is created which takes (what do to, control, guid2)
The first thread finishes:
it invokes a code into UI thread which takes results and the guid it got before execution
the invoked code checks that Control.Tag != guid1, makes no changes
The second thread finishes:
it invokes a code into UI thread which takes results and the guid it got before execution
the invoked code sees that Control.Tag == guid2, makes changes

Resources