Just wondered what that best approach is for this scenario - trying to databind to a collection which is being populated in another background thread.
My background thread is adding items to the collection in a processing loop which may run for several minutes. Every now and then it raises an event to the UI and passes a reference to the data from the collection, for the UI to visualize, (so the user can start to interact with what they have) and carries on processing.
Trouble is the UI starts to render the visualisation (which is quite complex itself), which involves a foreach() loop over the collection of data, and understandably that loop craps itself if my background thread changes the data in the collection during the enumeration.
So my brainstorming has gone like this:
pause the background thread; but I really don’t want to pause
take a duplicate snapshot copy of all, or some the data in each event, and databind to the snapshot. Doubles my memory usage but would probably work
implement some kind of lock{} on the syncroot or whatever of the collection so while the UI is updating the background process has to wait. Not confident about that working anyway
fire the event all the time and just pass one bit of data at a time, which has the same result as #2 but with more overhead..
cheers
ewart.
The easiest way to fix your problem is by changing the foreach loop to a for loop that loops from 0 to the number of items in your collection before you start to loop.
instead of:
var coll = new List<string>();
foreach (string item in coll)
{
//do your stuff
}
use:
var coll = new List<string>();
int length = coll.Count - 1;
for (int i = 0; i < length; i++)
{
}
OR
I don't know how complex your UI is but with WPF you can almost always change the code to advanced databinding bound to an ObservableCollection. The Observable collection triggers an update of the binding when an item is added or removed.
But to provide a solution for that requires a lot of information on how your UI is build.
Related
I have been dealing with multithreading issues for a while now. In the past few days I have been trying to ensure all my calls are thread safe. I have just run into an issue that has thrown me. Here is the scenario:
I am attempting to plot a waveform, using an Infragistics XamDataChart control, which is passed ~500 points/sec/waveform. Upon launch of the application I create objects that have an ObservableCollection property called WaveData and these properties are bound directly to the xaml in an itemscontrol. When the data comes in it is stored in a Queue and a spawned worker thread is used to dequeue the data and update the collection at the appropriate position.
Spawn worker thread:
QueueProcessThread = Task.Factory.StartNew(() => UpdateWaveFormCollections(WaveForms), tokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Code to update the collection which runs in a loop (some lines of code omitted for brevity):
waveForm.LastDisplayedTimeStamp = DateTime.Now; // move the last displayed time up
int collectionSize = waveForm.WaveData.Count;
while (waveForm.WaveDataBuffer.Count > 0 && waveForm.WaveDataBuffer.Peek().TimeStamp < waveForm.LastDisplayedTimeStamp)
{
if (waveForm.CurrentPosition >= collectionSize)
{
waveForm.CurrentPosition = 0;
}
waveForm.WaveData[waveForm.CurrentPosition] = waveForm.WaveDataBuffer.Dequeue();
waveForm.CurrentPosition++;
}
As you can see, I do not actually add/remove items to/from the collection but instead just update the item at a specific position. This is because I wanted it to look like a patient monitor at a hospital.
The problem I am running into is that I realized that I am updating this collection on a non UI thread and that collection is bound to the Infragistics LineSeries directly...and this is working. However, another graph using an Infragistics StepLineSeries throws an exception when I update that collection on a non UI thread which is expected. How is it possible that I am able to update a bound collection on a non UI thread? I am concerned by this because 1) occasionally I do get an error that a collection cannot be updated on a non UI thread and 2) when I switched the waveform update to be on the UI thread via a dispatcher the performance was so bad the GUI was unusable. I need to understand why this works so I know how to proceed. I do not want to deploy an application that might fail at any time due to thread mismanagement on my part. I am looking for possible reasons why/how it might be possible to update a bound ObservableCollection on a non UI thread. Any help/suggestions would be appreciated.
Maybe you need to look into using the Dispatcher (unless thats part of your code omitted).
You can use the Dispatcher method when you do an operation that will require code to be executed on the UI thread.
Maybe you could retrieve that data in the background worker thread and when you update your collection propagate changes back to the UI thread
e.g.
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => methodHere = 50));
Try using dispatcher invoke to make sure the collection will be accessed through UI thread, thus not allowing any other events to fire on a non UI thread.
Application.Current.Dispatcher.BeginInvoke(new Action(()=>
{
// code here to access collection
}));
The answer I was looking for is that you can update properties of a databound object and WPF will take care of the dispatching for you, however, you cannot update collections (add/remove/clear) from a non UI thread. In my waveform I was not adding points but updating the value of the item at a specific index. However, in the other scenario I was always adding and removing points.
I am reading a large Txt document into a WPF app for some serious swap/replacemnt operations. The files are actually 3D STP models so they are fairly large, but im working with them as raw text for this project. The files are read into List to avoid having to open them multiple times, and to make comparisons easier.
Anyway, I'm trying to get the listbox to scroll dynamically as lines are added to it, ala a console window so the user can see that something is happening since calculations can take a bit of time depending on filesize. I also added a progress bar to count away as the total line number is read through.
Neither my progress bar, nor ListBox seem to update as work progresses though. The final output simply lands in the listbox completed, and the progress bar goes from 0-max at the same time.
This is the gist of what I am doing, which is fairly simple:
foreach (string Line in OriginalSTPFile.Lines)
{
string NewLine = EvaluateString(Line); //string the modified to whatever here
pBar.Value++; //increment progressbar
OutputWindow.Items.Add(NewLine); //add line to the ListBox
}
I just want the listbox an progress bar to update in realtime as progress changes. I tried using:
Dispatcher.BeginInvoke(new Action(() => OutputWindow.Items.Add(NewLine));
But got the same results. Do I need a more elaborate method of multithreading here? I assumed the first method would've worked since I wasn't generating any cross-thread exceptions either.
This article will give you all the code that you need.
Backgroundworker with Progressbar
It describes very well what to do and which elements to use.
Dispatcher.BeginInvoke signals to invoke a method on the Dispatcher's thread. However, that's essentially like a post message, as it won't occur while the main thread is locked up doing work. And until the main thread is available again, it won't update the UI visually, even if you change values.
You'll need to perform the work in a background thread.
But to update the UI, you'll have to do so on the UI's main thread. This is a limitation of WPF. This is why you were directed to Dispatcher. I'm guessing someone assumed your work was already on a background thread.
To create a thread, you use Thread.Start passing it a delegate to perform. If you use a anonymous delegate or a lambda, you can refer to variables on the stack, but be aware that they will persist until the delegate quits. This is why you cannot use reference variables in a anonymous delegate.
Backgroundworker is a special type of background thread. It automates some of the expectations of a worker thread (notifying of completion, and updating on progress), but you can achieve the same results without it.
To update the UI during the thread's process, you'll need for that thread to be able to access the main UI thread. You can do that by passing it a dispatcher, referring to a dispatcher from outside the anonymous delegate, or by an object that contains a dispatcher. You can always read values from any object on any thread, so accessing the dispatcher by UIElement on another thread is fine.
To update the UI, you'll call Dispatcher.BeginInvoke with a delegate that entails the work to perform.
Here's psuedo-code of the overall scheme
class TestProgress
{
ProgressBar _ProgressBar;
void DoWork()
{
var worker = (Action)(() =>
{
int progress = 0;
// do stuff, delta is change in progress
progress += delta;
_ProgressBar.Dispatcher.BeginInvoke((Action)(() =>
{
_ProgressBar.Value = progress;
}));
});
Thread.Start(worker);
}
}
I have this:
Shows a waiting animation to 'block' the UI while performs a loading operation in the background.
At the end of the loading I call a method that instances a User Control and displays some data by using Bindings (and ObservableCollection among others)
This User Control gets displayed and user can interact with it, however the ObservableCollection seems to be stuck in another thread as it doesn't allow to add new items to it.
I've tried to update the UI at the Completed event of a BackgroundWorker, using Dispatcher, using DispatchTimer... all of this displays the User Control, but the ObservableCollection stays of out reach for adding.
The code that tries to add items to the collection is inside the UserControl.
The exact error is: "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread"
This does not happen if I don't do the loading in the background.
Thank you for any workaround for this.
By the way, trying to add the items using Dispatcher doesn't work either.
In other words, what I would like to do is to create an object in the UI Thread while being in the background... I know this may sounds silly.
You may have to check which Dispatcher you are using? In your case you could be referring to two different dispatchers.
Also why not use thread safe observable collection?
Usually I will create the objects on my UI thread, then populate them with data obtained from a background thread.
For example,
void async LoadButton_Clicked(object sender, EventArgs e)
{
MyCollection = new ObservableCollection<SomeItem>();
// Can also use a BackgroundWorker
var collectionData = await GetCollectionData();
foreach(var item in collectionData)
{
MyCollection.Add(item);
}
}
I'm using C# 5.0 async and await keywords for asynchronous operations, but you can also use a BackgroundWorker that does your background work.
You can also use Dispatcher.BeginInvoke() for some lighter background work (such as copying data into MyCollection), although for heavy work I find it still locks up the UI so I prefer to use background threads.
It is not possible to modify the contents of an ObservableCollection on a separate thread if a view is bound to this collection, instead you can override ObservableCollection and provide support for it and use it across your application.
This sample contains exactly what you want - http://tomlev2.wordpress.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/
When it comes to threads and ui-elements one of the most important rules to follow which may safe you a lot of trouble in the long run is to keep ui-element instantiation on the ui-thread. Surely you can manage that. And if you need to change those objects from another thread you can use the Dispatcher.
(The threading model reference may also be of interest)
Thank you everyone for your help... a guy from MS visited the company (sorry for the commercial annotation) to do other things, I stoled him and show this behavior. In a matter of 2 minutes founds the source of the problem... which I'm not sure to really understand.
It happens that I'm using an ICollectionView to display a sorted/filtered version of my problematic ObservableCollection. I was creating this ICollectionView in the constructor of my class, so at the moment of deserialization it was created in another thread. He suggested to move this creation to a further time in code (when the related property gets read). This solved the problem.
However the ObservableCollection, created in that other thread, now lets me add new item. Not sure why, but now it works.
Sorry for being this late and thank you again.
I have a WPF TreeView where each TreeViewItem has a checkbox. My implementation is based on the sample provided in Josh Smith's excellent article: http://www.codeproject.com/KB/WPF/TreeViewWithCheckBoxes.aspx. As in the article, the behavior I'm using is that when an item is checked or unchecked, all of its child items should be checked or unchecked, respectively.
The problem I have is that my TreeView can contain tens of thousands of items. So, if all the items are initially unchecked and I then check the root item, it may take 10-20 seconds before all the descendants are updated. This is too slow for my users and I'm wondering if there's a way to speed up the UI update. I'm fairly certain that the PropertyChanged events are the culprit here, since when I remove them, checking the root item is nearly instant, although the child checkboxes don't get updated. Is there a better way to handle updating multiple UI elements simultaneously? Note that I tried using container recycling but that didn't seem to help.
If you want to try reproducing this yourself, you can download the project in the aforemented article, and then in FooViewModel.cs, change the CreateFoos() function to use this code:
var root = new FooViewModel("Weapons");
root.IsInitiallySelected = true;
for (int i = 0; i < 400; i++)
{
var table = new FooViewModel("Table " + i);
for (int j = 65; j < 91; j++)
{
var charItem = new FooViewModel(Convert.ToChar(j).ToString());
for (int k = 0; k < 3; k++)
{
var deepItem = new FooViewModel("Item " + k);
charItem.Children.Add(deepItem);
}
table.Children.Add(charItem);
}
root.Children.Add(table);
}
root.Initialize();
return new List<FooViewModel> { root };
Thanks for any ideas,
-Craig
A UI presenting a tree view that contains 10,000 items is probably a UI that needs to be redesigned. (Though not necessarily - the browse-for-a-folder dialog essentially does this if your file system is hairy enough.)
Two things that you can try, assuming that you don't try virtualization:
Bind TreeViewItem.IsExpanded to a boolean property in your node view model. A node should only raise PropertyChanged events if its parent node's IsExpanded property is true. This will keep leaf nodes that aren't visible from raising events that the visual tree has to ignore. (You may also find that you'll need to have a node raise PropertyChanged on its IsChecked property whenever its parent's IsExpanded property becomes true.) This will still have problems when the tree is fully expanded, but unless you present the tree to the user in this state at the outset, the user will have to work at getting the control into a state where it starts experiencing performance issues.
Don't ever raise PropertyChanged events when a parent node updates its descendants. Instead, have the owning view model raise PropertyChanged on the property that the ItemsSource of the TreeView is bound to. This will force the whole control to re-render, but it's very likely that this will be faster than handling individual updates from all of the nodes. (Of course, you'll need to implement some way for a node to notify its parent that it just updated its descendants - e.g. raising an event - and some way to tell a node that it shouldn't raise this event if it's updating its descendants because its parent told it to.)
Unfortunately, WPF almost always runs into performance issues when dealing with as complex a visual tree as you seem to have. Also, if your viewmodel is only implementing INotifyPropertyChanged (rather than inheriting from DependencyObject) then updating 10000+ bindings is also generally going to be slow.
For reference, container recycling is only going to be useful if your ItemsPanel is virtualized (which I doubt it is considering your performance issue.)
Speaking of virtualization, that is probably the best way to address this issue. Unforunately, that likely means writing a custom VirtualizingPanel. You can get a start by following the chain here.
If you don't want to take that step (which is understandable,) then you should think about ways to offload some of the data from the tree, perhaps to some auxillary controls, so that you can limit how many items are updated with a single click.
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.