WPF: Accessing bound ObservableCollection fails althouth Dispatcher.BeginInvoke is used - wpf

I have the following:
public ICollectionView Children
{
get
{
// Determining if the object has children may be time-consuming because of network timeouts.
// Put that in a separate thread and only show the expander (+ sign) if and when children were found
ThreadPool.QueueUserWorkItem(delegate
{
if (_objectBase.HasChildren)
{
// We cannot add to a bound variable in a non-UI thread. Queue the add operation up in the UI dispatcher.
// Only add if count is (still!) zero.
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
if (_children.Count == 0)
{
_children.Add(DummyChild);
HasDummyChild = true;
}
}),
System.Windows.Threading.DispatcherPriority.DataBind);
}
});
return _childrenView;
}
}
It works great: HasChildren is run in a background thread which uses the dispatcher to insert its result into the variable used for the binding to the UI.
Note: _childrenView is set to this:
_childrenView = (ListCollectionView) CollectionViewSource.GetDefaultView(_children);
Problem:
If I call the Children property from another ThreadPool thread, I get a NotSupportedException in the line
_children.Add(DummyChild);
Exception text: "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."
Why? I have verified that that code is executed from the Dispatcher thread.

We've run into this problem before ourselves. The issue is twofold:
1- Make sure that any changes to the SourceCollection are on the main thread (you've done that).
2- Make sure that the creation of the CollectionView was also on the main thread (if it were created on a different thread, say in response to an event handler, this will not usually be the case). The CollectionView expects modifications to be on "its" thread, AND that "its" thread is the "UI" thread.

Related

Why can't I update my ObservableCollection with Filtered CollectionView attached during unit testing?

I've got an ObservableCollection in a ViewModel that I need to modify in code on a background thread (using a Task object). I've also got an ICollectionView attached to that collection. When I modify the collection at run-time, the code runs fine. In unit testing I get the error, "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."
I create the collection thusly (on the UI thread at run-time):
CashDeliveryDepots = new ObservableCollection<CashDeliveryDepot>();
FilteredCashDeliveryDepots = CollectionViewSource.GetDefaultView(CashDeliveryDepots);
and modify it using this code (which will be running on a non-UI thread at run-time):
CurrentDispatcher.Invoke(() =>
{
foreach (var depot in depots)
{
CashDeliveryDepots.Add(depot);
}
});
The CurrentDispatcher class executes code according to whether or not there is an application dispatcher.
internal static class CurrentDispatcher
{
internal static void Invoke(Action action)
{
if (Application.Current != null)
Application.Current.Dispatcher.Invoke(action);
else
action();
}
}
So at run-time the dispatcher is picked up correctly, the observable collection is updated and the filtered view updates too. When running the unit tests I get the exception at the point of trying to add a new item to the collection.
Can anyone suggest a way around this so I can get my tests working?

Calling thread cannot access object because different thread owns it

I thought I knew what causes this exception until I wrote this:
var menu = ViewConfigHelper.CreateObjectFromResource<Menu>(config, baseURI);
if (!menu.Dispatcher.CheckAccess())
{
throw new ArgumentException("Somethign wrong");
}
if (!LayoutRoot.Dispatcher.CheckAccess())
{
throw new ArgumentException("SOmethign wrong");
}
// exception throw here
LayoutRoot.Children.Insert(0, menu);
First line creates a Menu control from an embedded XAML file. Both CheckAccess calls return true. However, when last line is executed, an exception is thrown with the message "Caling thread cannot access object because differrent thread owns it." The code above is being executed within a method called immediately after InitializeComponent() that created LayoutRoot, on the same thread, I believe.
Someone please enlighten me. I am trying to create a multiple UI thread WPF app.
You are using CheckAccess() in reverse. You want to lose the ! signs before each check. See the example bit of code on the CheckAccess() MSDN page.
In the Winforms world you'd do a InvokeRequired() which is now the same thing as a !CheckAccess(). In your case because both values are returning true, and you are inverting them, neither if block is hit.
To expand a bit... in the Winforms world, the normal patter was:
if(InvokeRequired)
{
Invoke(...);
}
else
{
//do work
}
(or sometimes a return after invoke, if it was invoking the same method).
In WPF, CheckAccess() is similar to, but not identical to InvokeRequired... there for a pattern more along the lines of:
if (someUiControl.Dispatcher.CheckAccess())
{
//Doing an update from this thread is safe, so we can do so here.
}
else
{
// This thread does not have access to the UI thread.
// Call the update thread via a Dispatcher.BeginInvoke() call.
}
The key difference between is that InvokeRequired() returning true meant it was UNSAFE to do the update in the current thread... while a true from CheckAccess() means it is SAFE.

Confused by the behavior of Dispatcher.BeginInvoke()

Could someone shed some light on an issue I'm having?
I'm working on a wpf project. The scenario is as below:
I need to pop up a window(model window) on main UI thread and then close it. These works are started from another UI thread (to deter user from clicking on the main UI window.) then I close this window. The main code are displayed below. And it works.
As far as I know the close method would not get excuted before ShowDialog() returns (at least this is the case on UI thread, I mean code without dispatcher), does anyone have experience with multithread?
Window window;
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(() =>
{
//create a window and let user work from this thread
//code is omitted.
//create another window on main UI thread
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
window = new Window();
window.ShowDialog();
}));
//do some work here
Thread.Sleep(1000);
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
//Thread.Sleep(1000);
window.Close();
}));
});
thread.Start();
}
Thank you for your time!
So if I understand your question correctly, you're saying that this code works exactly the way you want, but you're just trying to understand how (and why) it works?
Here's how it works. First, your thread runs this code:
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
window = new Window();
window.ShowDialog();
}));
That queues your action on the main (UI) thread's dispatcher queue, and then returns immediately: your worker thread continues running.
When the Application first started up (typically via the compiler-generated code that initializes your App.xaml object, though you can also do it explicitly by calling Application.Run), it started its message loop, which goes something like this (pseudocode, very very simplified):
public class Application {
public void Run() {
while (!Exited && action = Dispatcher.DequeueAction())
action();
}
}
So at some point shortly after you queue the action, the UI thread will get around to pulling your action off the queue and running it, at which point your action creates a window and shows it modally.
The modal window now starts its own message loop, which goes something like this (again, very simplified):
public class Window {
public bool? ShowDialog() {
DisableOtherWindowsAndShow();
while (!IsClosed && action = Dispatcher.DequeueAction())
action();
EnableOtherWindowsAndHide();
return DialogResult;
}
}
Later, your worker thread runs this code:
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
window.Close();
}));
Again, your action is queued to the UI thread's dispatcher queue, and then the BeginInvoke call returns immediately and your worker thread continues running.
So sooner or later, the UI thread's message loop will get around to dequeuing and executing your action, which tells the window to close. This has essentially the same effect as the user clicking the title bar's "X" button, which of course is perfectly OK to do even when you're inside a modal dialog. This causes ShowDialog's message loop to terminate (because the window is now closed), at which point the dialog is hidden and the other windows are re-enabled, ShowDialog returns, your original (ShowDialog) action is complete and so returns, and control falls back to the original message loop in Application.Run.
Note that there's one dispatcher queue per thread, not one per message loop. So your "close" action goes into the same queue that your "show dialog" action did. It's a different piece of code doing the message-loop polling now (the one inside ShowDialog instead of the one inside Application.Run), but the basics of the loop are the same.
BeginInvoke is a non-blocking method; it adds the action to the dispatcher queue, and doesn't wait for its completion. You should use Invoke instead, which calls the method synchronously on the dispatcher thread.

Complex multi-threaded interface

First of all its not a splash-screen what i want... just to be clear... ok... lets go to the description of the problem:
i have a form that fire N number of threads (i dont know how many, the user must choose)... each thread has a object, and during several moments the objects may fire a event to signal some change... there must be a form for each thread to "report" the messages that the events are sending...
my problem is: the threads create the forms perfectally... but the desappear... out of nowhere... they appear on the screen... and vanish... poof.... gone! how can i avoid that undesired "disposing"?!?!
Your threads must either
use proper InvokeRequired + Invoke logic
or run their own MessagePump (Application.Run)
Which one did you (not) do?
If you create a form in a thread, the form will vanish when the thread is done. If you want the form to survive longer than that you need to either keep the thread alive, or create the form on the application's main thread. The latter would be preferable. Just make sure that each to hook up event listener for the object in the corresponding form, and use Invoke or BeginInvoke as needed when updating the form.
A simple example:
First a worker:
class Worker
{
public event EventHandler SomethingHappened;
protected void OnSomethingHappened(EventArgs e)
{
var evnt = SomethingHappened;
if (evnt != null)
{
evnt(this, e);
}
}
public void Work()
{
// do lots of work, occasionally calling
// OnSomethingHappened
}
}
Then, in a form we have an event handler for the SomethingHappened event:
public void SomethingHappenedHandler(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() => SomethingHappenedHandler(sender, e)));
return;
}
// update gui here
}
Then it's really just a matter of wiring it all together:
Worker w = new Worker();
ProgressForm f = new ProgressForm;
w.SomethingHappened += f.SomethingHappenedHandler;
f.Show();
Thread t = new Thread(w.Work);
t.Start();
Disclaimer: this sample is quickly tossed together and somewhat untested (sitting on the train, about to get off ;) ).
A Form must be hosted on a thread with a message loop. You can create a message loop by either calling Application.Run or Form.ShowDialog. However, unless you have really good reason for doing so I would avoid having more than one thread with a windows message loop.
I would also avoid creating N threads. There are better ways to parallelize N operations other than creating one thread per operation. To name only two: 1) queue a work item in the ThreadPool or 2) use the Task Parallel Library via the Task class. The problem with creating N threads is that each thread consumes a certain amount of resources. More threads means more resources will be consumed and more context switching will occur. More is not always better in the world of multithreading.

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

I get the following error
Cross-thread operation not valid:
Control accessed from a thread other
than the thread it was created on.
This is a callback from a wcf.
I have a textBox and I need to update the value and appendtext to it.
This value is coming back from another thread and updates the UI.
public CarStatus CarState
{
get
{
return _carState;
}
set
{
_carState;= value;
CarStatus tmpCarState;=null;
if (txtResult.InvokeRequired)
{
txtResult.Invoke(new MethodInvoker(() => { tmpCarState;=null;= _carState;}));
}
txtResult.AppendText(string.Format("Car status is: {0}{1}", tmpCarState, Environment.NewLine));
}
the following crashes!!
You forgot the else, as you're updating the text of the control via AppendText always, not just on non-invoke required.
And, well, I think you've got something wrong here: You're setting member variables through the invoker, but changing the actual WinForm component on any thread? You probably just want to but the whole block on Invoke.

Resources