I have to show a progress window in a different thread.
This is what I've done:
Thread loadT = new Thread(new ThreadStart(() =>
{
Loading ldd = new Loading();
ldd.SetContentMessage("Loading...");
ldd.Closed += (s, ec) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
ldd.ShowDialog();
}));
loadT.SetApartmentState(ApartmentState.STA);
loadT.Start();
//do something
loadT.Abort();
But I don't think this is the right way. I want to use this window for different processes and i also want to set the window on the top of the others. Which is the best approach?
Ty!
You should do this the other way around when you are waiting for data and stuff to load.
Loading ldd = new Loading();
ldd.SetContentMessage("Loading...");
ldd.ShowDialog();
Thread loadT = new Thread(new ThreadStart() =>
{
//Do stuff here
});
loadT.Start();
Then you can get setup some events and such to either post updates to the loading window, or just leave it as is. You can also either monitor the threads state within the Loading window and close itself when the thread is complete or close the window from the thread.
as an example you could modify Loading to take a Thread as its parameter.
Thread loadT = new Thread(new ThreadStart() =>
{
//Do stuff here
});
Loading ldd = new Loading(loadT);
ldd.ShowDialog();
You can then move the starting of the thread, and monitoring of the thread/closing the window into the Loading class and it can look after itself.
There are 900,000 ways you can do this. You can also use BackgroundWorkers instead of spawning a new Thread, or you can use async/await in .Net 4.5+. Threading like this has been exhaustively done in the past and there should be lots of resources on google to help you in whatever path you decide to take. The important takeaway from this is your window should really be on the UI thread, and your loading should be done on another thread, not the other way around.
You could refer to the following blog post for an example of how to launch a WPF window in a separate thread the right way: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/.
But you won't be able to mix controls that are created on different threads. A control can only be accessed on the thread on which it was originally created so it makes no sense to create a control on one thread and then trying to use it on another because this simply won't work because of the thread affinity.
Displaying a stand-alone top-level read-only window during the time a long-running operation is in progress is fine but you should probably close this window as soon as the operation has completed. You won't be able to move controls from this window to another one that was created on another thread anyway.
Related
I am just starting to deal with STA/MTA issues, so apologies for the simplicity of the question. I couldn't find an answer I could actually understand at the bottom of the ladder here.
I am writing a plugin for another piece of software, and come to a point in a worker thread that I need to create some UI elements. I understand that I cannot do that from inside the worker thread since it is not an STA thread, and that I need to get back to the Main (or just another?) STA thread to create the UI elements. Some clarifications would help greatly.
Do all STA threads have the same 'rights', i.e. if the main thread is STA and creates a Window, adds some UI elements to it. Then spawns off another STA thread, and that second thread likewise creates some UI elements, are they doing it in the same 'space' (poor word choice, but I don't know what else to use) and can access each other's UI elements without causing death and destruction? Or do I need to explicitly jump back to the Main/Original STA thread and ONLY ever create UI elements from THAT (not just ANY) STA thread?
If that is the case (only 1 STA thread is allowed to make UI elements) how do I do that correctly? I have seen many posts that related to this but for some reason I can't quite catch what's going on, and would love a REAL simple answer.
Please no 'Here's a cool slick way of doing...' I just need the simple way of at the point of execution where I need some UI elements jumping back over to the main STA thread if that's what's necessary.
If it is not necessary, then I will just make that worker thread an STA thread and continue on my way, is that fair? Or am I courting disaster?
if the main thread is STA and creates a Window, adds some UI elements to it. Then spawns off another STA thread, and that second thread likewise creates some UI elements, are they doing it in the same 'space' [snip...] and can access each other's UI elements without causing death and destruction?
If Thread A and B are both STA, then they can each create and update their own UI elements, but not eachothers. Any other threads that want to affect the UI have to use one of the BeginInvoke style methods to ask the appropriate thread to do the update.
If it is not necessary, then I will just make that worker thread an STA thread and continue on my way, is that fair? Or am I courting disaster?
You may not be able to make the worker thread an STA thread if it's been set to MTA and initialized. You may have to make a new thread.
How do you do it? It seems like you want to use WPF (System.Windows.*) for your UI - so If the app that you are "plugging into" is also using WPF, you should be able to access it and re use it's UI thread. If not, you can make a new thread, and create a new Application on it and call Run. This should set up a dispatcher for you.
Something like this (pseudo code sort-of copied from some working code I have elsewhere)
Dispatcher dispatcher = null; // we 'get to' the UI thread via the dispatcher
if(Application.Current) { // re use an existing application's UI thread
dispatcher = Application.Current.Dispatcher;
} else {
var threadReadyEvent = new ManualResetEvent(false);
var uiThread = new Thread(() => {
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
var application = new Application();
application.Startup += (sender, args) => {
dispatcher = application.Dispatcher;
threadReadyEvent.Set();
};
// apps have to have a "main window" - but we don't want one, so make a stub
var stubWindow = new Window {
Width = 1, Height = 1,
ShowInTaskbar = false, AllowsTransparency = true,
Background = Brushes.Transparent, WindowStyle = WindowStyle.None
};
application.Run(stubWindow);
}){ IsBackground = true };
uiThread.Start();
threadReadyEvent.WaitOne();
threadReadyEvent.Dispose();
}
dispatcher.Invoke(() => {
// ask the UI thread to do something and block until it finishes
});
dispatcher.BeginInvoke(() => {
// ask the UI thread to do something asynchronously
});
and so forth
If a thread creates a control. Only this specific thread can interact with it, even if there are other STA threads.
In WinForms you would invoke a method on the control: Control.Invoke.In WPF you have the dispatcher to do it: Dispatcher.Invoke.
WinForms:
form1.Invoke(/* a delegate for your operation */)
WPF:
window1.Dispatcher.Invoke(/* a delegate for your operation */)
What you do is instead of changing an object in a "single apartment" you ask (invoke) the STA thread in control of it to do it (the delegate you invoke) for you. You also have BeginInvoke for doing it asynchronously.
I'm new to WPF (and computer science in general) and I was given a small project from my boss where there are 5 tabs, each that go to a different site. After logging in, the user is directed to the website of the 1st tab. In the background, the 4 other tabs should be loading in the background.
Each has the following name: "tabItem1", "tabItem2", "tabItem3", "tabItem4" up to "tabItem5"
Inside each there is a up to "webBrowser5".
I think that I have to use threading to load the pages in the background, but I'm not sure how to implement it. I tried creating 4 different threads in the MainWindow such as:
public MainWindow()
{
InitializeComponent();
Thread thread1 = new Thread(Update1);
thread1.SetApartmentState(ApartmentState.STA);
thread1.Start();
Thread thread2 = new Thread(Update2);
thread2.SetApartmentState(ApartmentState.STA);
thread2.Start();
Thread thread3 = new Thread(Update3);
thread3.SetApartmentState(ApartmentState.STA);
thread3.Start();
Thread thread4 = new Thread(Update4);
thread4.SetApartmentState(ApartmentState.STA);
thread4.Start();
}
private void Update1()
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
tabItem2.Focus();
}
);
}
private void Update2()
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
tabItem3.Focus();
}
);
}
//...Goes up to Update4
This seems to only focus on the last thread and also it doesn't do it the background. I appreciate any guidance and help. Thanks!
That IS what the dispatcher is doing, operating on the main thread. You told the main thread to set focus because the object is on the main thread.
The problem:
The answer lies in why you must do this. You cannot operate on a windows control in any other thread than the thread it was created on. You also can't create a control in one thread, and set it as a child of a control in another thread.
What this means to you... is that what your boss asked you to do can't be done. The only thing you can do in the background is calculate algorithms. At best, you'll be able to load data, and operate on data, and interpret data, but if you want to have that data be displayed in, or converted into, windows controls, you must do that on the main thread.
The only solution:
However, you can have multiple UIThreads. Which means you can create multiple Windows. So, the must-do alternative is to create windows on separate threads for each tab content, then host the thread-windows on each tab.
Threading windows
Host process window
Cross thread hosting
I do not agree with Xaade, I think you can do what you need using only this code:
Dispatcher.BeginInvoke((Action)(() =>
{
// load the pages
}), DispatcherPriority.Background, null);
That code will be executed in background, so, there you can load every webBrowser you need.
You just need to specify the DispatcherPriority to Background.
A WPF app has an operation of loading a user control from a separate file using XamlReader.Load() method:
StreamReader mysr = new StreamReader(pathToFile);
DependencyObject rootObject = XamlReader.Load(mysr.BaseStream) as DependencyObject;
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
The process takes some time due to the size of the file, so UI becomes frozen for several seconds.
For keeping the app responsive I try to use a Background thread for performing the part of operation that is not directly involed in UI updating.
When trying to use BackgroundWorker I got an error: The calling thread must be STA, because many UI components require this
So, I went another way:
private Thread _backgroundThread;
_backgroundThread = new Thread(DoReadFile);
_backgroundThread.SetApartmentState(ApartmentState.STA);
_backgroundThread.Start();
void DoReadFile()
{
StreamReader mysr3 = new StreamReader(path2);
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action<StreamReader>)FinishedReading,
mysr3);
}
void FinishedReading(StreamReader stream)
{
DependencyObject rootObject = XamlReader.Load(stream.BaseStream) as DependencyObject;
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
}
This solves nothing because all time consuming operations remain in UI thread.
When I try like this, making all parsing in the background:
private Thread _backgroundThread;
_backgroundThread = new Thread(DoReadFile);
_backgroundThread.SetApartmentState(ApartmentState.STA);
_backgroundThread.Start();
void DoReadFile()
{
StreamReader mysr3 = new StreamReader(path2);
DependencyObject rootObject3 = XamlReader.Load(mysr3.BaseStream) as DependencyObject;
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action<DependencyObject>)FinishedReading,
rootObject3);
}
void FinishedReading(DependencyObject rootObject)
{
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
}
I got an exception: The calling thread cannot access this object because a different thread owns it. (in the loaded UserControl there are other controls present which maybe give the error)
Is there any way to perform this operation in such a way the UI to be responsive?
Getting the XAML to load an a background thread is essentially a non-starter. WPF components have thread affinity and are generally speaking only usable from the threads they are created one. So loading on a background thread will make the UI responsive but create components which then cannot be plugged into the UI thread.
The best option you have here is to break up the XAML file into smaller pieces and incrementally load them in the UI thread making sure to allow for a message pump in between every load operation. Possibly using BeginInvoke on the Dispatcher object to schedule the loads.
As you found out, you can't use XamlReader.Load unless the thread is STA and even if it is, you will have to have it start a message pump and funnel all access to the controls it created through that. This is a fundamental way of how WPF works and you can't go against it.
So your only real options are:
Break down the XAML into smaller pieces.
Start a new STA thread for each Load call. After Load returns, the thread will need to start a message loop and manage the controls it created. Your application will have to take into account the fact that different controls are now owned by different threads.
I don't have exact solution but you can get some direction from following links.
http://www.codehosting.net/blog/BlogEngine/post/Opening-WPF-Windows-on-a-new-thread.aspx
http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
System.Xaml has a Xaml​Background​Reader class, perhaps you could get that to work for you. Parser the XAML on the background thread but build the objects on the UI thread.
You can call a method that allows to give control to another thread:
http://msdn.microsoft.com/en-us/library/ms748331.aspx
It is called .Freeze()
I have a Windows Form Application (Form1) that allow the user to open another Forms (FormGraph). In order to open the FormGraph App I use a thread that open it.
Here is the code that the thread is running:
private void ThreadCreateCurvedGraph()
{
FormGraph myGraph = new FormGraph();
myGraph.CreateCurvedGraph(...);
myGraph.Show();
}
My problem is that myGraph closed right after it's open.
1) Does anyone know why this is happening and how to make myGraph stay open?
2) After the user closed myGraph, How do I terminate the thread?
Many thanks!
The problem is not in the posted snippet. You'll need to start a new message loop with Application.Run() or Form.ShowDialog(). You'll also need to take care of thread properties so it is suitable to act as a UI thread. For example:
Thread t = new Thread(() => {
Application.Run(new Form2());
// OR:
//new Form2().ShowDialog();
});
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
There are some awkward choices here. The form cannot be owned by any form on your main thread, that usually causes Z-order problems. You'll also need to do something meaningful when the UI thread's main form is closed. Sloppily solved here by using IsBackground.
Windows was designed to support multiple windows running on one thread. Only use code like this if you really have to. You should never have to...
The main problem you ahve is that you do not establish a message pump in the new thread.
Check
Run multiple UI Threads
for a good overview how to run a high perforamnce user interface using multiple threads (one per form / group of forms).
What you basically miss is the call to Application.Run to set up the message pump on the separate UI thread.
I think once the last form of a message pump closes - it will dispose itself and end.
Note that all this ASSUMES you WANT to open the window in a separate UI thread... otherwise you need to invoke back to the main UI thread for the creation and all manipulation of the window, so it gets attached to the existing message pump. There are GOOD cases for both - one keeps thigns simple, the other allows a LOT more performance as every window has a separate message pump and can thus act individually - this is for example used a lot in trading applications which may need to update graphs on a number of screens and havea bottleneck if running single threaded in the UI.
As a rule of thumb you should avoid manipulating the UI from threads (creating a form is a sort of manipulation to the UI). You should always manipulate the UI from the main thread.
The form is closing because the thread has finished and is therefore free'd along with its resouces (the form). To make the thread stay running you need a loop
e.g.
private void ThreadCreateCurvedGraph()
{
FormGraph myGraph = new FormGraph();
myGraph.CreateCurvedGraph(...);
myGraph.Show();
while (myGraph.IsOpen)
{
//process incoming messages <- this could be fun on a thread....
}
}
You'll need a method of setting IsOpen (like a timeout or a button) and obviously you'll need to actually create IsOpen as a property of the form and set it to true when the form is created.
I'll add here the same as other users... You should have a good reason for not using the main thread.
If it takes a while to prepare the data for the form, you can do that in a separate thread to keep the application responsive. When the data is ready you can return the object to the main thread an let it show it.
You should declare a variable for the object in the form rather than locally in the method, so that it survives when you exit the thread.
When you are ready to show the form, you can use the Invoke method to make a method call that will be executed in the main thread.
do not create and show forms in non-main thread. do it in main form thread.
Or do this:
private void ThreadCreateCurvedGraph()
{
FormGraph myGraph = new FormGraph();
myGraph.CreateCurvedGraph(...);
Application.Run(myGraph);
}
but first version is better
Why are you creating a form on a new thread? There are times you need to use a new thread but other times you can use form.ShowDialog() on the main thread.
What about if you show the form as if it was a Dialog? You can use
private void ThreadCreateCurvedGraph()
{
FormGraph myGraph = new FormGraph();
myGraph.CreateCurvedGraph(...);
myGraph.ShowDialog();
}
This way the call will block until the myGraph form is closed. As you have the myGraph created on a separated thread calling the blocking ShowDialog should block only that thread.
Perhaps this is garbage collection:
After ThreadCreateCurvedGraph() exits, myGraph goes out of scope and closes.
You need to organise a way of the thread to hold on to the instance and wait (using a blocking wait) for it to close.
Edit: For instance add:
Application.Run(myGraph)
to the end of the method.
(See comments from TomTom)
I have recently started programming in WPF and bumped into the following problem. I don't understand how to use the Dispatcher.Invoke() method. I have experience in threading and I have made a few simple Windows Forms programs where I just used the
Control.CheckForIllegalCrossThreadCalls = false;
Yes I know that is pretty lame but these were simple monitoring applications.
The fact is now I am making a WPF application which retrieves data in the background, I start off a new thread to make the call to retrieve the data (from a webserver), now I want to display it on my WPF form. The thing is, I cannot set any control from this thread. Not even a label or anything. How can this be resolved?
Answer comments:
#Jalfp:
So I use this Dispatcher method in the 'new tread' when I get the data? Or should I make a background worker retrieve the data, put it into a field and start a new thread that waits till this field is filled and call the dispatcher to show the retrieved data into the controls?
The first thing is to understand that, the Dispatcher is not designed to run long blocking operation (such as retrieving data from a WebServer...). You can use the Dispatcher when you want to run an operation that will be executed on the UI thread (such as updating the value of a progress bar).
What you can do is to retrieve your data in a background worker and use the ReportProgress method to propagate changes in the UI thread.
If you really need to use the Dispatcher directly, it's pretty simple:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => this.progressBar.Value = 50));
japf has answer it correctly. Just in case if you are looking at multi-line actions, you can write as below.
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
this.progressBar.Value = 50;
}));
Information for other users who want to know about performance:
If your code NEED to be written for high performance, you can first check if the invoke is required by using CheckAccess flag.
if(Application.Current.Dispatcher.CheckAccess())
{
this.progressBar.Value = 50;
}
else
{
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
this.progressBar.Value = 50;
}));
}
Note that method CheckAccess() is hidden from Visual Studio 2015 so just write it without expecting intellisense to show it up. Note that CheckAccess has overhead on performance (overhead in few nanoseconds). It's only better when you want to save that microsecond required to perform the 'invoke' at any cost. Also, there is always option to create two methods (on with invoke, and other without) when calling method is sure if it's in UI Thread or not. It's only rarest of rare case when you should be looking at this aspect of dispatcher.
When a thread is executing and you want to execute the main UI thread which is blocked by current thread, then use the below:
current thread:
Dispatcher.CurrentDispatcher.Invoke(MethodName,
new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.
Main UI thread:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background, new Action(() => MethodName(parameter)));
The #japf answer above is working fine and in my case I wanted to change the mouse cursor from a Spinning Wheel back to the normal Arrow once the CEF Browser finished loading the page. In case it can help someone, here is the code:
private void Browser_LoadingStateChanged(object sender, CefSharp.LoadingStateChangedEventArgs e) {
if (!e.IsLoading) {
// set the cursor back to arrow
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => Mouse.OverrideCursor = Cursors.Arrow));
}
}