昨天我遇到How to update gui in c# wpf from asynchronous method callback
的问题没有人帮助我,但我发现了一些有用的东西:
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();
今天我在更改窗口时遇到问题,因为该操作出错:
跨线程操作无效:从创建它的线程以外的线程访问控件。
当我从同步方法调用它时,代码可以工作,但是当我异步调用它时它不会。
更改窗口的方法:
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);
}
注释代码是我尝试过的,未注释的行适用于同步方法。我已经在WPF中看到这些问题由dispatcher.invoke()处理。在我使用的窗口控件表单中:
this.Invoke((MethodInvoker)delegate
{
//changing UI
});
我不知道如何使其发挥作用。任何帮助将不胜感激。
答案 0 :(得分:3)
Dispatcher
具有线程关联性 - 它为实例化它的线程提供服务。您已经创建了自己的线程,因此该线程将需要自己的调度程序。相反,您正在使用与另一个线程(您的主线程)相关联的调度程序。
我看到你在评论中尝试过各种各样的内容,但是当你尝试使用findVisualParent
时,你是否已注释掉它并不清楚。因此,我无法确切地告诉您出错的地方,但可以告诉您,如果没有通过其调度程序执行此操作,您尝试从未创建的线程访问UI组件。
您需要跟踪代码并确切地找到哪条线路失败,看看您正在使用哪个线程并验证您使用的是正确的调度程序。
答案 1 :(得分:0)
如果您使用这种方法,则应先使用CheckAccess()方法,然后再调用窗口上的方法进行显示和关闭。
如果调用线程有权访问此对象,则CheckAccess()返回true。否则返回false,您需要使用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);
}
}
答案 2 :(得分:0)
我刚刚在一个全局的地方保存了UI调度程序,当我想从一个线程切换时,我称之为:
public void ChangetoWindow(){
dispatcher.Invoke(()=&gt; navigationService.Navigate(new Window())); }