Reputation:
I am trying to make a simple application in WPF which will open a new window in a thread it's behaving oddly.
ArrayList formArray = new ArrayList();
Thread th;
Window1 vd;
public void Start()
{
vd = new Window1();
formArray.Add(vd);
vd.ShowDialog();
}
public void StartCall()
{
th = new Thread(new ThreadStart(Start));
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
StartCall();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
((Window1)(formArray[0])).Show();
}
Window1 code is
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
}
When trying to open it again, it just throws an error The calling thread cannot access this object because a different thread owns it
.
When trying to use dispatcher.. invoke... all these things didn't help. To make it even weirder, this same code worked in a Windows Forms application.
Maybe it's related to this line? th.SetApartmentState(ApartmentState.STA);
?
It might be this guys, but if I won't add it, it will also fail with an error that
Additional information: The calling thread must be STA, because many UI components require this.
Upvotes: 2
Views: 566
Reputation: 400
Edit: Added the force run on dispatcher on your thread. I also added a Display method to show the dialog depending on the dispatcher who is calling. Hope that help !
Also, as explained here: Dispatcher.Run
You should shutdown the dispatcher of the corresponding thread when you are done.
MainWindow:
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
StartCall();
}
ArrayList formArray = new ArrayList();
Window1 vd;
Thread th;
public void Start()
{
vd = new Window1();
formArray.Add(vd);
vd.ShowDialog();
System.Windows.Threading.Dispatcher.Run(); //ok this is magic
}
public void StartCall()
{
th = new Thread(new ThreadStart(Start));
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
StartCall();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
((Window1)(formArray[0])).Display();
}
Window1:
void Window1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
}
public void Display()
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke((Action)Display);
return;
}
this.Show();
}
Upvotes: 1
Reputation: 34198
You can't call .Show
on your window from a thread other than the one it was created on (that's basically what the error message is telling you!). Fortunately, as you suggested, this is what the dispatcher is for: to marshal calls onto the correct thread. But you have to use the correct dispatcher for the job!
Each control in WPF (including a Window) has a .Dispatcher
property that gets the Dispatcher for that control's thread. My guess is that you were using the one from your main window when trying to re-open the dialog - which is the wrong one. Instead, if you use this in your Button_Click
you will have more luck:
var window = (Window1)formArray[0];
window.Dispatcher.Invoke(window.Show); // note: use the dispatcher that belongs to the window you're calling
(NOTE: this isn't to say that this is a typically useful/recommended design pattern. In fact, it's often going to cause more problems than it solves. But, it's certainly something you can choose to do.)
Upvotes: 0