I need to show window with spinner, during processing some action.
This Window should be model, so I am using ShowDialog() method:
void ShowDlg() {
Thread WindowThread = new Thread(() =>
{
SpinnerWindow spinnerWindow = new SpinnerWindow();
spinnerWindow.ShowDialog();
System.Windows.Threading.Dispatcher.Run();
});
WindowThread.SetApartmentState(ApartmentState.STA);
WindowThread.Start();
}
Then after some process is done, I am trying to close this modal window:
WindowThread.Interrupt();
if (!WindowThread.Join(2000))
{
WindowThread.Abort();
}
and everything works well, until I try to call ShowDlg() second time. I am getting next exception:
The calling thread cannot access this object because a different thread owns it.
What I am doing wrong, maybe incorrect close of created thread?
You are probably calling the ShowDialog from within another thread again. When that's the case you should invoke your main form like this:
mainForm.Invoke(spinnerWindow.ShowDialog());
Related
void itemCommand_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
{
var thread = new Thread(() =>
{
if (LoginCheck())
{
ItemWindow itw = new ItemWindow();
//Dispatcher.CurrentDispatcher.Invoke((System.Action)(() =>
//{
itw.Show();
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
//}));
Dispatcher.Run();
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
I keep getting error message of "The calling thread cannot access this object because a different thread owns it." on line "itw.show();" when this function called twice. It works fine for the first call, and after the window is closed and trying to open again, it fails. As I commented out "Invoke" method, it doesn't work with Dispatcher either. Please help me to find the solution.
Thank you.
----------------- Edit
The reason why I'm creating a new thread is because it is an Excel addin. I can't create windows from main thread which is the excel that collides with windows if i create them from main thread.
The thing I don't understand is that why does the new instance(ItemWindow) from new thread collide with old thread.
I created a simple test method in a new application that is called when I click the (only) button on my main form. The method looks like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(() =>
{
Window1 window = new Window1();
window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
window.Show();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
Window1 is a window class I made that has nothing but a single TextBlock on it. I can click that button as many times as I want, and it continues to open new windows with no issues (regardless whether or not I close the previous one first).
I suspect the issue is occurring in code that you are not showing us somewhere. You need to be very careful that nothing on your new thread attempts to access anything UI related from your main thread. Windows running on separate threads cannot communicate with each other unless they go through the dispatcher of the other thread. The exception you are seeing is thrown when any method or property of a DispatcherObject is accessed from a thread other than the one that created the object.
Taking a step back, why is it important that the new window be on its own thread? Unless the new window is going to monopolize the thread, it will probably run fine on the main thread. If you are running some long blocking operation, perhaps that operation alone should be moved to a thread rather than the entire window. I don't know what you are doing exactly, but it is something to think about.
EDIT: Realizing that you may not be running in a typical WPF application (looks like you might be in an Office plugin), I updated my test to launch the windows completely standalone on their own threads. However, I am still able to launch two windows in a row with no issues.
Here is my new test. This method and the test class Window1 are the entirety of my application.
[STAThread]
public static int Main(string[] args)
{
ThreadStart threadFunc = () =>
{
Window1 window = new Window1();
window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
window.Show();
System.Windows.Threading.Dispatcher.Run();
};
Thread thread = new Thread(threadFunc);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
thread = new Thread(threadFunc);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return 0;
}
So, there appears to be nothing inherently wrong with what you are trying to do, nor do I see any obvious issue in your code. I suspect that there is some invalid cross-thread communication happening somewhere in your custom window while it is being shown. (Either that, or you are running into an issue specific to Office plugins.)
You are trying to connect the event handler to the ItemWindow after it is already visible.
You need to switch the order from:
ItemWindow itw = new ItemWindow();
itw.Show();
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
to
ItemWindow itw = new ItemWindow();
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
itw.Show();
A possible cause are dependency properties. Dependency properties are a bit picky when it comes to threading.
Even if you don't define your own DepProps, your window will still have some and there is no way to get rid of them.
DepProps have a significant downside: they are thread bound and cannot be accessed from another thread. Which thread holds all the rights is defined by the thread that initializes the DepProps, in your case the first call to new ItemWindow(). After that first call your thread is set and you need that thread to access your DepProps.
For the first window that is no problem but the second one clearly had a different thread. I don't know exactly how DepProps do that, but you might try capturing and restoring the synchronization context of the first thread. Another option would be to capture the dispatcher of the first thread (not the main thread one)
I have written a WordAddIn that allows the user to call on some metadata for the current document. Via a custom button in the ribbon, they can call a WPF. The WPF calling is realized as follows:
System.Windows.Application app = null;
and then in the method called by the button:
if (app == null)
{
app = new System.Windows.Application { ShutdownMode = ShutdownMode.OnExplicitShutdown };
app.Run();
}
MainWindow win = new MainWindow(graph);
app.Dispatcher.Invoke((Action)(() => { win.Show(); }));
The first time the button is clicked after Word started, nothing happens and it becomes impossible to edit content in the word document. The second time the button is clicked the WPF object loads and is shown, this works for any button click afterwards too. So it seems the first time the dispatcher is called, it hangs. How do I prevent this?
I'm not sure why you want to dispatch the call anyway since this is the same thread. You only need to marshall calls to UI thread when the "current" thread is not a UI thread, which is not your case - you only have one thread.
Secondly, Application.Run is a blocking method and should not be called in the Add-In context. You can't create a WPF application inside Add-In application. BTW, Application.Run always runs on the current thread, which in your case is the same one Word Add-In runs on. And this how it is supposed to be.
If I understand you correctly, you are creating a WPF application because you don't want to use WinForms technology but WPF. Do it without Application.Run and do not dispatch calls because there is no reason to do so.
I got help with this in the Microsoft WPF forums. Creating and calling app in a new thread does the trick, as app does indeed block the current thread it is called in.
if (app == null)
{
Thread thread = new Thread(new ThreadStart(() =>
{
app = new System.Windows.Application { ShutdownMode = ShutdownMode.OnExplicitShutdown };
autoResetEvent.Set();
app.Run();
}));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
else
{
autoResetEvent.Set();
}
autoResetEvent.WaitOne(); //wait until app has been initialized on the other thread
app.Dispatcher.Invoke((Action)(() => { new MainWindow(graph).Show(); }));
}
I am using this wonderful framework for doing modal dialogs in WPF.
Using this framework, I am trying to get a modal dialog to overlay another modal dialog when a user clicks a button from within the first dialog. There is an example of this in the DemoApp for this framework which just uses the _dialogmanager to first pop up one MessageDialog and then another.
The code that does this from the DemoApp looks like this:
private void ShowLayeredDialog()
{
_dialogManager
.CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
.Show();
ThreadPool.QueueUserWorkItem(o =>
{
Thread.Sleep(2000);
_dialogManager
.CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
.Show();
});
}
I tried to do something similar but instead of using the method call to CreateMessageDialog, I wanted to use their CreateCustomContentDialog(), which takes an object and displays its contents (provided its a UIElement) in a modal view.
So, having already called the _dialogManager to get me into the first modal view, I created a button on that view that would spawn a new CustomContentDialog like so using a technique similar to their DemoApp code:
ThreadPool.QueueUserWorkItem(o => _dialogManager.CreateCustomContentDialog(new SpikePhaseView(), DialogMode.Ok).Show());
Unfortunately, I get the exception 'The calling thread must be STA, because many UI components require this' on the constructor for SpikePhaseView(), which is a vanilla UserControl.
So having researched this error here and here I implemented the un-accepted, but highly up-voted solution from the second link of setting the ApartmentState(ApartmentState.STA) like so:
var test = new Thread(() => _dialogManager.CreateCustomContentDialog(new SpikePhaseView(), DialogMode.Ok).Show());
test.SetApartmentState(ApartmentState.STA);
test.Start();
But then somewhere down WpfDialogManagment framework code, I get this error 'The calling thread cannot access this object because a different thread owns it.' on this block of code:
public void SetCustomContent(object content)
{
CustomContent.Content = content;
}
Above, the CustomContent (A System.Windows.Controls.ContentControl) is being set to my SpikePhaseView object.
Edit
In the DemoApp they are able to launch two modal dialogs successfully (without error). Why can't I have one UserControl (view) spawn another without having this conflict over which thread is setting the content of this CustomContext object?
It seems like setting the ApartmentState helped get me past the first error but if this all boils down to using the Dispatcher, can someone provide an example of how I can use it in my situation to fire off a call to launch the second modal view?
Thanks
You don't want to have multiple UI threads in your application unless you're really, really sure that you need it. It will greatly complicate matters if you always need to be thinking about which thread owns which controls. It's much easier if there is just one UI thread and it owns everything.
Rather than trying to make the new thread that you create an STA thread, just ensure that your second popup is created/accessed/shown from the main UI thread.
If you're using C# 5.0 this is very easy:
//Consider changing the return type to Task
//if this method is not used as an event handler
private async void ShowLayeredDialog()
{
_dialogManager
.CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
.Show();
await Task.Delay(2000);
_dialogManager
.CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
.Show();
}
The await call will ensure that the remainder of the method (the creation of the second dialog) is run as a continuation of the first task, and it will ensure that it runs in the captured SynchronizationContext (in this case representing the UI thread). The end result is that the code doesn't sit there blocking a background thread, keeps you at just one declaration scope, will perform better, handles errors better, is less typing, etc.
The C# 4.0 method of doing this is a bit more code:
private void ShowLayeredDialog()
{
_dialogManager
.CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
.Show();
Task.Delay(2000).ContinueWith(t =>
{
_dialogManager
.CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
.Show();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
That's (more or less) what the first snippet is turned into by the compiler.
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 am developing a plugin for an application, that application "consumes" my code (classLibrary), and executes the Init() method inside his own Thread.
Inside the Init() I have a while(true) statement, so that my plugin can run continuously.
Yesterday, I started to make a windowsForm for configuration of my plugin (using XML), and now I want to show it, but it keeps vanishing. My code is as follows:
Doing this will show the form, but, it wont re-paint because it is launched on that same thread as the while(true) is.
MaForm settingsForm = null;
void init(){
While(true){
if(settingsForm == null){
settingsForm = new MaForm();
settingsForm.show();
}
}
}
Version that shows, but then vanishes.
MaForm settingsForm = null;
Thread worker = null;
void init(){
While(true){
if(worker == null){
worker = new Thread(new ThreadStart(formStuff));
worker.Start();
}
}
}
void formStuff()
{
if(settingsForm == null){
settingsForm = new MaForm();
settingsForm.show();
}
}
What am I doing wrong? Is there something about Threading I am missing?
What do you guys think?
The thread is starting, showing your form, then finishing up and shutting down (which closes the form).
Showing a form on a separate thread is nearly always problematic. Forms require a message pump to be running - so they typically will only work properly if they're started and run on the GUI thread.
One option would be to invoke the function to show your form onto your main thread. This will make your form load (and run) on the main thread.
Alternatively, you can start an entire message pump on the form's thread, and set the thread to STA (this is important). However, I suggest avoiding this approach if possible.
you can try this: create the form, enter the infinite loop, call DoEvents() so that your form can process windows messages:
if(settingsForm == null){
settingsForm = new MaForm();
settingsForm.show();
}
while (settingsForm != null && settingsForm.Visible)
{
System.Windows.Forms.Application.DoEvents();
}
EDIT: may be you can replace the true condition by a check on the SettingsForm visibility. When the form is closed, it's a waste to stay in an infinite loop.
A good way of dealing with threading issues in C# is to comment out using System.Threading; at the top of your classes and forms. You may have some compelling reason for showing forms with a Thread, but probably not, since Form.Show() is not a blocking call.
if you're trying to show a form from your Main() method, try using ShowDialog() instead. This call will block until the form is closed.