I have seen some code samples of multi-threaded UIs in WPF using multiple windows where each window runs on it's own UI thread. I'm curious though - is there a way to accomplish this with embedded controls?
For example lets say I have usercontrol1 and usercontrol2 both embedded in Window1. Usercontrol1 starts to spin and blocks the main UI thread. The window and usercontrol2 are effected. Is there a way to make it so even if usercontrol1 blocks, Window1 + usercontrol2 are still responsive?
Let us assume we don't control the developers of usercontrol1 - so we can't tell them to make their control behave.
Let us also assume the exchange of data between usercontrol1 + 2 is a must.
Should I be exploring something like AddIn's?
You can't "run" the controls on another thread (as with Winforms, the controls themselves must be created and run on the same thread as the top-level parent), but there's nothing stopping you from initiating actions on another thread from a user control. You just have to ensure that you use the Dispatcher to Invoke any operations that will have a direct effect upon the control itself. Behind-the-scenes processing can be done entirely on another thread, though; it's just the physical updating of the UI that has to be invoked back via the Dispatcher.
EDIT: After the question was edited, no, there is no way to move all of the "work" that a particular component performs to another thread. If you can't control the development of the component in question, then you're at the mercy of the developer and where he decides to execute the code.
As on the top you have visual (Window) which is inheriting from DispatcherObject. It is designed as STA threading model, in which single thread is accessing the object methods and properties.
That is why both controls must be created in the same thread as Window.
If you need to exchange data between controls and you are not controlling developers of those controls I would consider using PRISM or other MVVM framework. In prism you have EventAggregator which is basically subscriber publisher pattern your UserControl1 can publish something and the other one if it's subscribed will receive that.
Related
Can I read the state of controls (not change it) in non-UI threads like this
if (!string.IsNullOrWhiteSpace(notifyIcon.BalloonTipText))
{
// ...
}
Is it safe?
See this artice from Visual Studio Magazine about Multithreading in WinForms.
As explained on page 2, one of the problems of accessing UI controls from a thread other than the thread that created the control is that that thread doesn't have a message pump (or at least not necessarily has one). "Reading" UI controls state is done through window messages (for example, WM_GETTEXT) just as writing to them, and this is wrong in the same way.
There is a property named "InvokedRequired" in every control. Check for this property, if it is false you can call method in regular way, otherwise call invoke method with the help of delegate from the control you are trying to access.
I inherited a winforms app. It uses a third-part-closed control that renders documents and photos... It has only sync methods for opening a document. The problem is that my clients are dealing with really big documents (in the area of 2GB!!!) and opening these docs really "block" the UI thread... which is bad...
Common sense would make you think "Just off-load it to a background thread" but the question is "HOW"! See, to alter the control (because calling "Open" causes it to be altered) I need to Invoke it, and that causes the code to run o UI thread again... locking it up...
So I turned the table upside down. What if instead of creating the control on the main thread and passing it to a background thread for processing, I could create the control on the background thread, load it up (avoiding this way the cross-thread exception) and, when done, feed it to the main thread?!?
Right now what I need is to know how to definitively handle a control to another thread, and not only temporally...
I'm not sure if this is possible but you could try to:
create a new form on a secondary thread (this form will host your fancy control)
load the document from this secondary UI. It will be blocked but you can hide it and only display a
loading message on the main UI.
when the job is finished transfer the 'work' to main UI and main thread.
It's just an idea.
What you are asking to do is impossible. A Winforms control's thread affinity is determined when that control is created, and it cannot be changed.
The best solution is to not use that control. I doubt there's anything it does that cannot be implemented correctly and competently by someone else.
If you are okay running a completely different window in a second STA thread, then that would be the next best thing. That particular window will still be frozen while the document loads, but at least your main UI would still be okay. Note that you should not try to mix and match controls from different threads in the same window; that will lead to all kinds of headaches.
Finally, as a complete hack, you might consider going ahead and calling this Open() method in a background thread in spite of the control being owned by the main UI thread. On the admittedly shaky assumption that the only time that control will actually attempt to access the UI component itself would be at the very end of the Open() method operation, you can go ahead and catch the InvalidOperationException that is thrown, and use that as your signal that the document loading has completed. Then just invalidate the control in the main UI thread.
I'd give the odds of this last suggestion working no better than 50/50. It will depend on what the control actually does with the loaded data, and if it's some kind of composite control where it's relying on actually taking the result of its loading and copying that to a control as part of the Open() method, that part might fail and the control would not wind up properly initialized.
I have a problem and after three days I still can not find a answer.
I am creating a usercontrol. This control has two controls hosted on it. One is an edit field and the other is a margin that will hold line numbers and other user added stuff.
What I am trying to do is instantiate these to separate controls in their own threads and perform all tasks on these threads eg painting and updating. The usercontrol thread will simply filter messages and direct same to the correct control. I will be doing this by overriding the WndProc method.
Doing all the message stuff I am fine with however how do I instantiate the controls. Once the thread that creates these controls finishes wont the threads die and the controls became inaccessible. Sorry no code at the moment, I tend to do a lot of research before any coding but I can't seem to find anything that can be of help.
This is not going to work very well. All UI elements (forms, controls, etc.) have thread affinity requirements that dictate that they can only ever be accessed from the thread that created them. Trying to embed a control hosted on one thread in another control from another thread is an effort in futility. Even if you can get it work (and use the term "work" loosely here) the results may be unpredictable at best. You need to rethink your approach. It would be much better to host all of the controls on the same thread and then shuttle off any time consuming operations to a worker thread. Remember, keep the worker thread busy with non-UI related operations only. Once the operation is complete then you can marshal over the results to the UI thread where it can dispatched appropriately to the controls.
I know I need to create and add elements to another element on the main UI thread in WPF and you can easily do this when on another thread using the Dispatcher.
However I would like to build my elements off the UI thread then add them all in one batch if possible.
This is because I am building thousands of elements and the couple seconds it takes on the UI thread freezes the whole application.
Is there any way to do this?
I don't think so; however, you can create a smoother user experience by gradually loading the elements without making the GUI hang completely. This can be done by subscribing to the CompositionTarget.Rendering event, which will be called each time a frame is rendered. If you maintain a list of the view models (and corresponding control types) that you need to add, you can create some (say, fifty) of them and add them the visual tree inside the event handler. The next time the event is invoked, you add fifty more, and so on.
It could be possible, what you do is you create two UI threads. On one UI thread you build up your window and show some kind of progress indication (progress bar) on the other UI thread. Make your window visible when you've built it up.
http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
I recommend to you to use some composite application patter, like Prims. See Patterns For Building Composite Applications With WPF.
Prism allows to decouple your apps components, and also load them on demands.
Also you can see: Prism
Hope this helps...
I'm having a problem with handling an event on a different thread from where it is raised. The object that is handling the event is not an UI object however, so I can't use Invoke to execute the delegate and automatically switch to the UI thread for event handling.
The situation is as following: I have an MDI application containing multiple forms. Each form has it's own controller class that handles communication between the coupled form and external objects. All forms are either overview or detail forms (e.g. ContactsOverview & ContactDetail) and share the same data.
In the situation where the error occurs the forms appear in a wizard-like sequence, say a detail form is followed by an overview form. In the detail form data used on the following overview form is changed and before switching to the overview form these changes need to be reflected there. An event is raised from the detail form and handled by the controller for the overview form which does the necessary updating of UI elements.
Now the saving of the changed data in the detail form can take a while so it is necessary that the UI remains responsive and other parts of the application can still be used. This is why a backgroundworker is started to handle this. When the data is saved the event is raised on the background thread. The controller for the overview handles this but when the UI needs to be update there are of course cross-thread exceptions.
So what I need is a way to raise the event on the UI thread, but since the handling doesn't happen on a UI element there's no way to switch threads automatically using Invoke.
From searching around the web I've found one possible solution which is using the producer/consumer pattern. But this would require each controller to listen to a queue of events in a separate thread as far as I understand. Since it's an MDI application there could theoretically be any number of forms with controllers and I don't want to be starting up that many threads.
Any suggestions are welcome. If there would be a way to avoid using the backgroundworker alltogether that would be a suitable solution as well.
Thanks for reading,
Kevin
You can use SynchronizationContext, specifically SynchronizationContext.Current, to post messages to the main synchronization context (which is the main thread for a GUI application).
Unfortunately I don't know enough about the class and its usage to say this is a definite solution. In particular, I don't know what you should do if you don't require the main thread to handle your events, but instead a particular thread.
Perhaps the WindowsFormsSynchronizationContext class can help you out, it has a public parameterless constructor, I'm thinking it might associate it with the current thread, so if you construct that object from the thread that owns the controller, and give it to the background thread code, it might work.
You can have an event on the background objec that the UI element subscribes to. In the event handler (of the subscription - so it is part of the window code) you can then to the invocation. This is how I solve this.
You can try this flag but I don't think it's the best idea, just a work around.
You could also try to instantiate the issuing objects in a non-graphical thread, which may fix your problem.
One more thing, can't you have your UI component handle RunWorkerCompleted (with indirections) ?