Reputation: 20286
Yesterday I had problem with How to update gui in c# wpf from asynchronous method callback
No one helped me, but I found something that worked:
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(delegate
{
this.TargetWindow = new MessageListsWindow();
}));
th.SetApartmentState(System.Threading.ApartmentState.STA);
th.Start();
Today I have a problem with changing the window, because there is an error in that operation:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.
The code works when I call it from synchronous method, but when I call it asynchronously it doesn't.
The method to change window:
public void NavigateToWindow(CustomWindow targetWindow)
{
CustomWindow currentWindow = findVisualParent<CustomWindow>(this);
if(currentWindow != null)
{
currentWindow.Close();
//targetWindow.Dispatcher.Invoke(new Action(() => targetWindow.Show()));
//Application.Current.Dispatcher.BeginInvoke(new Action(() => targetWindow.Show()));
//targetWindow.Dispatcher.Invoke(new Action(() => targetWindow.Show()));
//currentWindow.Dispatcher.Invoke(new Action(() => targetWindow.Show()));
targetWindow.Show();
}
}
private T findVisualParent<T>(DependencyObject child)
where T : DependencyObject
{
// get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
// we’ve reached the end of the tree
if (parentObject == null) return null;
// check if the parent matches the type we’re looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
// use recursion to proceed with next level
return findVisualParent<T>(parentObject);
}
Commented code is what I've tried, uncommented line works for synchronous methods. I've read that in WPF these problems are handled by dispatcher.invoke(). In window control forms I used:
this.Invoke((MethodInvoker)delegate
{
//changing UI
});
I don't know what to do to make it work. Any help would be appreciated.
Upvotes: 0
Views: 1237
Reputation: 193
I just saved the UI dispatcher in a global place the when I wanted to switch from a thread I call something like this:
public void ChangetoWindow() {
dispatcher.Invoke(() => navigationService.Navigate(new Window())); }
Upvotes: 0
Reputation: 178790
A Dispatcher
has thread affinity - it services the thread on which it is instantiated. You've created your own thread, so that thread will need its own dispatcher. Instead, you're using a dispatcher that is associated with another thread (your main thread).
I see you've tried various things in the comments, but it's not clear whether you commented out the findVisualParent
when you did try them. Therefore, I can't tell you exactly where you've gone wrong, but can tell you that you're trying to access a UI component from a thread it wasn't created on without doing so via its dispatcher.
You need to trace through your code and find exactly which line is failing, see what thread you're on and validate that you're using the correct dispatcher.
Upvotes: 3
Reputation: 35554
If you use such an approach you should use CheckAccess() method first before you call methods on your windows to show and close.
CheckAccess() returns true if the calling thread has access to this object. Otherwise it returns false and you need to dispatch your method call with the Dispatcher.
if(currentWindow != null)
{
if(currentWindow.CheckAccess()) {
currentWindow.Close();
}
else {
currentWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
()=>{currentWindow.Close();},null);
}
if (targetWindow.CheckAccess()) {
targetWindow.Show();
}
else {
targetWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
()=>{targetWindow.Show();},null);
}
}
Upvotes: 0