how to use data from one Thread in another [duplicate] - c#

I've got an ObservableCollection<A> a_collection; The collection contains 'n' items. Each item A looks like this:
public class A : INotifyPropertyChanged
{
public ObservableCollection<B> b_subcollection;
Thread m_worker;
}
Basically, it's all wired up to a WPF listview + a details view control which shows the b_subcollection of the selected item in a separate listview (2-way bindings, updates on propertychanged etc.).
The problem showed up for me when I started to implement threading. The entire idea was to have the whole a_collection use it's worker thread to "do work" and then update their respective b_subcollections and have the gui show the results in real time.
When I tried it , I got an exception saying that only the Dispatcher thread can modify an ObservableCollection, and work came to a halt.
Can anyone explain the problem, and how to get around it?

New option for .NET 4.5
Starting from .NET 4.5 there is a built-in mechanism to automatically synchronize access to the collection and dispatch CollectionChanged events to the UI thread. To enable this feature you need to call BindingOperations.EnableCollectionSynchronization from within your UI thread.
EnableCollectionSynchronization does two things:
Remembers the thread from which it is called and causes the data binding pipeline to marshal CollectionChanged events on that thread.
Acquires a lock on the collection until the marshalled event has been handled, so that the event handlers running UI thread will not attempt to read the collection while it's being modified from a background thread.
Very importantly, this does not take care of everything: to ensure thread-safe access to an inherently not thread-safe collection you have to cooperate with the framework by acquiring the same lock from your background threads when the collection is about to be modified.
Therefore the steps required for correct operation are:
1. Decide what kind of locking you will be using
This will determine which overload of EnableCollectionSynchronization must be used. Most of the time a simple lock statement will suffice so this overload is the standard choice, but if you are using some fancy synchronization mechanism there is also support for custom locks.
2. Create the collection and enable synchronization
Depending on the chosen lock mechanism, call the appropriate overload on the UI thread. If using a standard lock statement you need to provide the lock object as an argument. If using custom synchronization you need to provide a CollectionSynchronizationCallback delegate and a context object (which can be null). When invoked, this delegate must acquire your custom lock, invoke the Action passed to it and release the lock before returning.
3. Cooperate by locking the collection before modifying it
You must also lock the collection using the same mechanism when you are about to modify it yourself; do this with lock() on the same lock object passed to EnableCollectionSynchronization in the simple scenario, or with the same custom sync mechanism in the custom scenario.

Technically the problem is not that you are updating the ObservableCollection from a background thread. The problem is that when you do so, the collection raises its CollectionChanged event on the same thread that caused the change - which means controls are being updated from a background thread.
In order to populate a collection from a background thread while controls are bound to it, you'd probably have to create your own collection type from scratch in order to address this. There is a simpler option that may work out for you though.
Post the Add calls onto the UI thread.
public static void AddOnUI<T>(this ICollection<T> collection, T item) {
Action<T> addMethod = collection.Add;
Application.Current.Dispatcher.BeginInvoke( addMethod, item );
}
...
b_subcollection.AddOnUI(new B());
This method will return immediately (before the item is actually added to the collection) then on the UI thread, the item will be added to the collection and everyone should be happy.
The reality, however, is that this solution will likely bog down under heavy load because of all the cross-thread activity. A more efficient solution would batch up a bunch of items and post them to the UI thread periodically so that you're not calling across threads for each item.
The BackgroundWorker class implements a pattern that allows you to report progress via its ReportProgress method during a background operation. The progress is reported on the UI thread via the ProgressChanged event. This may be another option for you.

With .NET 4.0 you can use these one-liners:
.Add
Application.Current.Dispatcher.BeginInvoke(new Action(() => this.MyObservableCollection.Add(myItem)));
.Remove
Application.Current.Dispatcher.BeginInvoke(new Func<bool>(() => this.MyObservableCollection.Remove(myItem)));

Collection synchronization code for posterity. This uses simple lock mechanism to enable collection sync. Notice that you'll have to enable collection sync on the UI thread.
public class MainVm
{
private ObservableCollection<MiniVm> _collectionOfObjects;
private readonly object _collectionOfObjectsSync = new object();
public MainVm()
{
_collectionOfObjects = new ObservableCollection<MiniVm>();
// Collection Sync should be enabled from the UI thread. Rest of the collection access can be done on any thread
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{ BindingOperations.EnableCollectionSynchronization(_collectionOfObjects, _collectionOfObjectsSync); }));
}
/// <summary>
/// A different thread can access the collection through this method
/// </summary>
/// <param name="newMiniVm">The new mini vm to add to observable collection</param>
private void AddMiniVm(MiniVm newMiniVm)
{
lock (_collectionOfObjectsSync)
{
_collectionOfObjects.Insert(0, newMiniVm);
}
}
}

I used a SynchronizationContext:
SynchronizationContext SyncContext { get; set; }
// in the Constructor:
SyncContext = SynchronizationContext.Current;
// in the Background Worker or Event Handler:
SyncContext.Post(o =>
{
ObservableCollection.AddRange(myData);
}, null);

MicrosoftDocs
Platform code for UI (layout, input, raising events, etc.) and your app’s code for UI all are executed on the same UI thread
ObservableCollection is raising CollectionChanged event when one of these actions occurs: Add, Remove, Replace, Move, Reset.. And this event must be raised on UI thread, otherwise, an exception will occur in the caller thread
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
And the UI won't updated.
If you want to update the UI from a background thread, Run the code in Application's dispatcher
Application.Current.Dispatcher.Invoke(() => {
// update UI
});

#Jon answer is good but it lacks a code sample:
// UI thread
var myCollection = new ObservableCollection<string>();
var lockObject = new object();
BindingOperations.EnableCollectionSynchronization(myCollection, lockObject );
[..]
// Non UI thread
lock (lockObject)
{
myCollection.Add("Foo")
}
Also note that the CollectionChanged event handler will still be called from the non UI thread.

Related

Attach FixedDocument created in another thread [duplicate]

A common exception one can get when working with multiple threads in WPF is:
The calling thread cannot access this object because a different thread owns it
What are the options to deal with this properly?
Depending on the situation there are various options:
Accessing a control from another thread
e.g. updating a TextBlock with progress information.
Data Binding:
In this case the easiest thing you can do is avoiding the direct interaction with the control. You can just bind the property you want to access or modify to an object whose class implements INotifyPropertyChanged and then set the property on that object instead. The framework will handle the rest for you. (In general you rarely should need to interact with UI-elements directly, you can almost always bind the respective properties and work with the binding source instead; one case where direct control access may be necessary is control authoring.)
There are some cases where data binding alone is not enough, for example when trying to modify a bound ObservableCollection<T>, for this you need...
Dispatching:
You can dispatch your accessing code to the thread owning the object, this can be done by calling Invoke or BeginInvoke on the Dispatcher owning the object being accessed (getting this Dispatcher is possible on another thread).
e.g.
new Thread(ThisThreadStart).Start();
void ThisThreadStart()
{
textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test"));
}
If it is not clear on which thread a method is executed you can use Dispatcher.CheckAccess to either dispatch or execute an action directly.
e.g.
void Update()
{
Action action = () => myTextBlock.Text = "Test";
var dispatcher = myTextBlock.Dispatcher;
if (dispatcher.CheckAccess())
action();
else
dispatcher.Invoke(action);
}
If an object is not a DispatcherObject and you still need the associated Dispatcher you can use Dispatcher.CurrentDispatcher in the thread creating the object (so doing this in the method being executed by a thread will not do you any good). For convenience as you usually create objects on the application's main UI thread; you can get that thread's Dispatcher from anywhere using Application.Current.Dispatcher.
Special cases:
BackgroundWorker
Move any control access to ProgressChanged as it occurs on the thread that created the instance (which should of course be the UI-thread)
Timers
In WPF you can use the DispatcherTimer for convenience, it does the dispatching for you so any code in Tick is invoked on the associated dispatcher. If you can delegate the dispatching to the data binding system you of course can use a normal timer as well.
You can read more about how the Dispatcher queue works and WPF threading in general on MSDN.
Accessing an object created on another thread
e.g. loading an image in the background.
If the object in question is not Freezable you should in general simply avoid creating it on another thread or restricting access to the creating thread. If it is Freezable you just need to call Freeze to make it accessible to other threads.
Accessing a data object from another thread
That is, the type whose instance is being updated is user-code. If an exception is thrown this situation probably came about by someone using DependencyObject as base type for a data class.
This situation is the same as accessing a control and the same approaches can be applied but usually it should be avoided in the first place. Granted, this allows for simple property change notifications via dependency properties and those properties can also be bound but often enough this is just not worth giving up thread-independency. You can get change notifications from INotifyPropertyChanged and the binding system in WPF is inherently asymmetrical, there always is a property that is bound (target) and something that is the source for this binding. Usually the UI is the target and the data is the source, meaning that only UI components should need dependency properties.
That would be several hundred lines of code, for something I "figured out".
But the summary is:
App_OnStartup
generate a background thread
in the callback,
Call
Application.Current.MainWindow.Dispatcher.CheckAccess() - gets the exception
Application.Current.Dispatcher.CheckAccess() does not
I have a udp listener object that communicates through events where the method/callbacks are +='ed in my mainWindow wpf .cs file.
The event handler functions are called with parameters, one being the message I want displayed in a listbox in the mainWindow.cs
Using the information in this thread by H.B. above;
I have added, tested and handled the crossthread in wpf in my eventhandler callback using the following code, but I use a real message not a hard coded one:
listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere")));
UPDATE:
This is better because you can put more things in the anonymous function.
listBox1.Dispatcher.Invoke((Action)delegate
{
listBox1.Items.Add(e.ReaderMessage);
});

Add items to a list from the thread that initialized it

I have a WPF application that communicate with the server via WCF.
I execute a method on the remote server and the callback method initializes a list with the results which running on a different thread. - This is fine and this is exactly the purpose of my application.
But when I want to add some more items to this list, it throws an exception that I can't add items from a different thread which has initialized this list.
public ObservableCollection<ListBoxItemVM<T>> Items
{
get { return items; }
set
{
// This section runs on a separate thread.
items = value;
notify("Items");
if (allItems == null)
allItems = new ObservableCollection<ListBoxItemVM<T>>(items.Clone());
// I want to save the current context here and use it on the AddItem method
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(Items);
view.Filter = searchFilter;
}
}
public void AddItem(ListBoxItemVM<T>
{
this.items.Add(item); // The following exception throws here
}
Exception: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
I'm looking for some way to save the thread (or thread's ExecuteContext) that the list was initialized with, and add items to that list with this thread/context.
It should be mentioned that there is nothing to do with the UI thread, I handled the marshaling with the UI thread on another region in my code.
I've tried to marshal the this.items.Add(item); code with the UI SynchronizationContext, buy they are different so it fails.
Thanks
Starting from .NET 4.5 there is a built-in mechanism to automatically synchronize access to the collection and dispatch CollectionChanged events to the UI thread. To enable this feature you need to call BindingOperations.EnableCollectionSynchronization from within your UI thread.
EnableCollectionSynchronization does two things:
Remembers the thread from which it is called and causes the data binding pipeline to marshal CollectionChanged events on that thread.
Acquires a lock on the collection until the marshalled event has been handled, so that the event handlers running UI thread will not attempt to read the collection while it's being modified from a background thread.
Very importantly, this does not take care of everything: to ensure thread-safe access to an inherently not thread-safe collection you have to cooperate with the framework by acquiring the same lock from your background threads when the collection is about to be modified.
Therefore the steps required for correct operation are:
Decide what kind of locking you will be using
This will determine which overload of EnableCollectionSynchronization must be used. Most of the time a simple lock statement will suffice so this overload is the standard choice, but if you are using some fancy synchronization mechanism there is also support for custom locks.
Create the collection and enable synchronization
Depending on the chosen lock mechanism, call the appropriate overload on the UI thread. If using a standard lock statement you need to provide the lock object as an argument. If using custom synchronization you need to provide a CollectionSynchronizationCallback delegate and a context object (which can be null). When invoked, this delegate must acquire your custom lock, invoke the Action passed to it and release the lock before returning.
Cooperate by locking the collection before modifying it
You must also lock the collection using the same mechanism when you are about to modify it yourself; do this with lock() on the same lock object passed to EnableCollectionSynchronization in the simple scenario, or with the same custom sync mechanism in the custom scenario.

WPF How to update GUI when background thread finishes creating collections?

So I want to load my data collections in a background thread and then bind my treeview to the new collection (rather than having the background thread queue things up on the dispatcher every time it wants to add an item to the list [sounds inefficient]).
Is this possible? I create the new data structure and it is output as pd.result on a background thread. When the UI thread checks that the dialog box has closed, it should then set
ModuleHierarchyVM.TopLevelModules = pd.Result as ObservableCollection<ModuleViewModel>;
after this the event OnLoadVCD is called. I have an event handler that then tries to set
a treeview's itemsource to the new collection.
this.AvailableModulesTreeView.ItemsSource = gvvm.ModuleHierarchyVM.TopLevelModules;
This crashes with the error:
"The calling thread cannot access this object because a different thread owns it."
Not even sure how to debug it, the call stack doesn't give any real details.
However, if I just set the Itemsource to a blank new empty collection like so:
this.AvailableModulesTreeView.ItemsSource = (IEnumerable<object>)new List<object>();
it doesn't crash (but then it's not displaying my data either). Any ideas what could be causing the crash?
I thought it might be that I was updating the UI from the wrong thread, so I tried both calling the dispatcher with begininvoke, and checking that I am indeed the UI thread with dispatcher.checkaccess(). so that does not seem to be the issue. However, I really don't know what is going on.
Another way I could implement this is to just make my parsing routine just update the original data structure that is bound to the treeview by caling dispatcher on each item as it is added to the observable collection. However, even if that is the only solution, I really dislike not knowing why something doesn't work. In my mind, it seems reasonable to just create an entirely new data structure on a different thread, and then rebind the new datastructure to the treeview, discarding the old one. It also seems cleaner to me than dozens of 1 line ObservableCollectionInstance.Add calls being placed on the dispatcher while parsing through a file on the background thread.
Full Code:
method called by UI thread
public bool LoadPortInterface(string VCDFileName)
{
ProgressDialog pd = new ProgressDialog("Loading File: ", VCDFileName);
pd.Owner = Application.Current.MainWindow;
pd.WindowStartupLocation = WindowStartupLocation.CenterOwner;
ModuleHierarchyVM.TopLevelModules.Clear();
VCDData TempVCDOutput = null;
Func<object> handler = delegate
{
return VCDParser.ParseVCDFileForAllPorts(VCDFileName, this, pd.Worker, out TempVCDOutput);
};
pd.RunWorkerThread(handler);
pd.ShowDialog();
if (pd.DialogResult == true)
{
ModuleHierarchyVM.TopLevelModules = pd.Result as ObservableCollection<ModuleViewModel>;
VCDOutput = TempVCDOutput;
}
OnLoadVcd();
}
Response to OnLoadVCD event handler in graphviewer:
void gvvm_LoadVCDEvent(object sender, EventArgs e)
{
this.AvailableModulesTreeView.ItemsSource = gvvm.ModuleHierarchyVM.TopLevelModules;
}
I think it will be easer to use TPL "Task Parallel Library"
For example, if you want to create new thread you can create it as task.
var task = Task.Factory.StartNew(() =>
{
// write what you want to do here
}
You can get more examples from this link Task Parallelism (Task Parallel Library)
So, to update the UI from thread, you can run this thread in the same of UI thread as below:
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
_taskFactoryWrapper.StartTask(() => DoSomeWork(viewSettings.AnyValue)).ContinueWith(task =>
{
viewSettings.Result= task.Result;
},TaskContinuationOptions.AttachedToParent)
.ContinueWith(t => EndingUploadingProgress(viewSettings), uiContext);
So you can create a TaskScheduler associated with the current UI thread.]
You are most likely creating, reading or modifying the ObservableCollection on a thread other than the UI thread. Also make sure you are not adding to or removing from the ObservableCollection on anything other than the UI thread.
To debug this, put a breakpoint wherever you access/modify the observable collection and note the thread number (Thread window in VS) that hits that breakpoint. It should always be the same.
You could use another structure (List/Array) to hold the results, then call back to the UI thread to update/create the ObservableCollection. Updating the ObservableCollection is inexpensive, even for hundreds of items.
What does get expensive is that ObservableCollection will raise a change event on every change, which may be handled by the UI components to change their layout, which has to be done on the UI thread anyway. This UI event handling is why ObservableCollection prevents you from modifying across threads.
If you are adding/removing a large number of items, it may be better to create a new collection and reassign the DataSource. If you always do this, you can use a List instead. ObservableCollection is for when you want to modify the list and have the control only change the smallest amount possible. Changing the DataSource (eg to List) will clear the control and rebuild it, which may be better for many changes.
See:
Updating an ObservableCollection in a separate thread
How do I update an ObservableCollection via a worker thread?
What's the best way to update an ObservableCollection from another thread?

C# thread safety (in particular MVVM/WPF)

I'm wondering what I need to do to make models thread safe in MVVM. Say I had the following class, which is instantiated as a singleton:
public class RunningTotal: INotifyPropertyChange
{
private int _total;
public int Total
{
get { return _total; }
set
{
_total = value;
PropertyChanged("Total");
}
}
...etc...
}
My view model exposes it via a property:
public RunningTotal RunningTotal { get; }
And my view has a textblock bound to it, i.e. {Binding Path=RunningTotal.Total}.
My app has a background thread that periodically updates the value of Total. Assuming nothing else updates Total, what (if anything) should I do to make all this thread-safe?
Now, what if I wanted to do something similar but using a property of type Dictionary<>, or ObservableCollection<>? Which members (add, remove, clear, indexer) are thread-safe? Should I use a ConcurrentDictionary instead?
My app has a background thread that periodically updates the value of Total. Assuming nothing else updates Total, what (if anything) should I do to make all this thread-safe?
For scalar properties, you don't need to do anything special; the PropertyChanged event is automatically marshaled to the UI thread.
Now, what if I wanted to do something similar but using a property of type Dictionary<>, or ObservableCollection<>? Which members (add, remove, clear, indexer) are thread-safe? Should I use a ConcurrentDictionary instead?
No, this is not thread-safe. If you change the content of an ObservableCollection<T> from a background thread, it will break. You need to do it on the UI thread. An easy way to do it is to use a collection that raises its events on the UI thread, like the one described here.
As for Dictionary<TKey, TValue>, it doesn't raise a notification when its content changes, so the UI is not notified anyway.
Say that there are two threads updating Total, and you want to log all changes to _total in the PropertyChanged method. Now there is a race condition in which PropertyChanged can miss a value. This happens when a thread blocks in the middle of calling set_Total. It updates _total but does yet call PropertyChanged. In the meantime, another thread updates _total to another value:
thread1: _total = 4;
thread2: _total = 5;
thread2: PropertyChanged("Total");
thread1: PropertyChanged("Total");
Now, PropertyChanged is never called with the value of 4.
You can solve this by passing the value to the PropertyChanged method, or by using a lock in the setter.
Since you say that you have one thread which updates this property, there is no possibility for a race condition. This is only the case when multiple threads (or processes) update the same thing at the same time.
The model should be written in a thread-safe way just like any code must be; it's up to you to determine whether you are using locks, concurrent containers or anything other to do it. The models are just library code, which (almost) shouldn't know that its functionality is going to be consumed by a MVVM application.
The VMs, however, have to work in the UI thread. This means that typically they cannot rely that the events from model are coming in the UI thread, so they have to marshal the calls or store them in a task queue if the subscribed events are coming not at the UI thread.
So, the VM is the place which should care about thread safety in a specific way, more than the model needs to.
The View code, in turn, can usually live in happy ignorance about all the threading issues: it gets all the messages/calls/events/whatever in the dedicated UI thread, and makes it own calls in the UI thread as well.
Specifically for your case, your code is not the model but VM, right? In this case you have to fire your events in the UI thread, otherwise the View will be unhappy.
This question provides a thread-marshalled version of the ObservableCollection
How do you correctly update a databound datagridview from a background thread
However, you still need to worry about contention between threads, which would require you to lock resources when they are updated, or use something like Interlocked.Increment.
If one thread is updating, and another is reading, there exists the possiblity that a read is done half way through an update (For example, an Int64 is being modified. The first half (32 bits) has been updated in one thread, and before the second half has been updated, the value is read from a second thread. A completely wrong value is read)
This may or may not be a problem depending on what your application is going to do as a result. If the wrong value will be flashed on the GUI for 1 second, then its probably not a big deal, and the performance penalty of the lock can be ignored. If your program is going to take action based on that value, then you probably need to lock it down.
A simple answer is that you need to schedule the property updates in UI Thread through UI Thread's Dispatcher. This will put updates operation in a queue which will not crash the application.
private void handler(object sender, EventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate { updates(); });
}
private void updates() { /* real updates go here */ }
Some thing more elaborate that it is actually so simple... When instantiating your viewmodel in your view just pass the dispatcher down in the ctor.
ServerOperationViewmodel ViewModel;
public pgeServerOperations()
{
InitializeComponent();
ViewModel = new ServerOperationViewmodel(Dispatcher);
}
Then in your View model:
Dispatcher UIDispatcher;
public ServerOperationViewmodel(Dispatcher uiDisp)
{
UIDispatcher = uiDisp;
}
And use it like a normal UI dispatcher.
UIDispatcher.Invoke(() =>
{
.......
});
I will admit that I am still fairly new to MVVM, but I don not think this breaks the MVVM motto.

How do I update an ObservableCollection via a worker thread?

I've got an ObservableCollection<A> a_collection; The collection contains 'n' items. Each item A looks like this:
public class A : INotifyPropertyChanged
{
public ObservableCollection<B> b_subcollection;
Thread m_worker;
}
Basically, it's all wired up to a WPF listview + a details view control which shows the b_subcollection of the selected item in a separate listview (2-way bindings, updates on propertychanged etc.).
The problem showed up for me when I started to implement threading. The entire idea was to have the whole a_collection use it's worker thread to "do work" and then update their respective b_subcollections and have the gui show the results in real time.
When I tried it , I got an exception saying that only the Dispatcher thread can modify an ObservableCollection, and work came to a halt.
Can anyone explain the problem, and how to get around it?
New option for .NET 4.5
Starting from .NET 4.5 there is a built-in mechanism to automatically synchronize access to the collection and dispatch CollectionChanged events to the UI thread. To enable this feature you need to call BindingOperations.EnableCollectionSynchronization from within your UI thread.
EnableCollectionSynchronization does two things:
Remembers the thread from which it is called and causes the data binding pipeline to marshal CollectionChanged events on that thread.
Acquires a lock on the collection until the marshalled event has been handled, so that the event handlers running UI thread will not attempt to read the collection while it's being modified from a background thread.
Very importantly, this does not take care of everything: to ensure thread-safe access to an inherently not thread-safe collection you have to cooperate with the framework by acquiring the same lock from your background threads when the collection is about to be modified.
Therefore the steps required for correct operation are:
1. Decide what kind of locking you will be using
This will determine which overload of EnableCollectionSynchronization must be used. Most of the time a simple lock statement will suffice so this overload is the standard choice, but if you are using some fancy synchronization mechanism there is also support for custom locks.
2. Create the collection and enable synchronization
Depending on the chosen lock mechanism, call the appropriate overload on the UI thread. If using a standard lock statement you need to provide the lock object as an argument. If using custom synchronization you need to provide a CollectionSynchronizationCallback delegate and a context object (which can be null). When invoked, this delegate must acquire your custom lock, invoke the Action passed to it and release the lock before returning.
3. Cooperate by locking the collection before modifying it
You must also lock the collection using the same mechanism when you are about to modify it yourself; do this with lock() on the same lock object passed to EnableCollectionSynchronization in the simple scenario, or with the same custom sync mechanism in the custom scenario.
Technically the problem is not that you are updating the ObservableCollection from a background thread. The problem is that when you do so, the collection raises its CollectionChanged event on the same thread that caused the change - which means controls are being updated from a background thread.
In order to populate a collection from a background thread while controls are bound to it, you'd probably have to create your own collection type from scratch in order to address this. There is a simpler option that may work out for you though.
Post the Add calls onto the UI thread.
public static void AddOnUI<T>(this ICollection<T> collection, T item) {
Action<T> addMethod = collection.Add;
Application.Current.Dispatcher.BeginInvoke( addMethod, item );
}
...
b_subcollection.AddOnUI(new B());
This method will return immediately (before the item is actually added to the collection) then on the UI thread, the item will be added to the collection and everyone should be happy.
The reality, however, is that this solution will likely bog down under heavy load because of all the cross-thread activity. A more efficient solution would batch up a bunch of items and post them to the UI thread periodically so that you're not calling across threads for each item.
The BackgroundWorker class implements a pattern that allows you to report progress via its ReportProgress method during a background operation. The progress is reported on the UI thread via the ProgressChanged event. This may be another option for you.
With .NET 4.0 you can use these one-liners:
.Add
Application.Current.Dispatcher.BeginInvoke(new Action(() => this.MyObservableCollection.Add(myItem)));
.Remove
Application.Current.Dispatcher.BeginInvoke(new Func<bool>(() => this.MyObservableCollection.Remove(myItem)));
Collection synchronization code for posterity. This uses simple lock mechanism to enable collection sync. Notice that you'll have to enable collection sync on the UI thread.
public class MainVm
{
private ObservableCollection<MiniVm> _collectionOfObjects;
private readonly object _collectionOfObjectsSync = new object();
public MainVm()
{
_collectionOfObjects = new ObservableCollection<MiniVm>();
// Collection Sync should be enabled from the UI thread. Rest of the collection access can be done on any thread
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{ BindingOperations.EnableCollectionSynchronization(_collectionOfObjects, _collectionOfObjectsSync); }));
}
/// <summary>
/// A different thread can access the collection through this method
/// </summary>
/// <param name="newMiniVm">The new mini vm to add to observable collection</param>
private void AddMiniVm(MiniVm newMiniVm)
{
lock (_collectionOfObjectsSync)
{
_collectionOfObjects.Insert(0, newMiniVm);
}
}
}
I used a SynchronizationContext:
SynchronizationContext SyncContext { get; set; }
// in the Constructor:
SyncContext = SynchronizationContext.Current;
// in the Background Worker or Event Handler:
SyncContext.Post(o =>
{
ObservableCollection.AddRange(myData);
}, null);
MicrosoftDocs
Platform code for UI (layout, input, raising events, etc.) and your app’s code for UI all are executed on the same UI thread
ObservableCollection is raising CollectionChanged event when one of these actions occurs: Add, Remove, Replace, Move, Reset.. And this event must be raised on UI thread, otherwise, an exception will occur in the caller thread
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
And the UI won't updated.
If you want to update the UI from a background thread, Run the code in Application's dispatcher
Application.Current.Dispatcher.Invoke(() => {
// update UI
});
#Jon answer is good but it lacks a code sample:
// UI thread
var myCollection = new ObservableCollection<string>();
var lockObject = new object();
BindingOperations.EnableCollectionSynchronization(myCollection, lockObject );
[..]
// Non UI thread
lock (lockObject)
{
myCollection.Add("Foo")
}
Also note that the CollectionChanged event handler will still be called from the non UI thread.

Categories

Resources