导航到从线程调用的WPF窗口
本文关键字:WPF 窗口 调用 线程 导航 | 更新日期: 2023-09-27 18:10:10
昨天我遇到了如何在c# wpf中从异步方法回调更新gui的问题
没人帮我,但我找到了有用的方法:
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
});
我不知道该怎么做才能让它起作用。
Dispatcher
具有线程亲和性——它服务于实例化它的线程。您已经创建了自己的线程,因此该线程将需要自己的调度程序。相反,您使用的是与另一个线程(您的主线程)相关联的调度程序。
我看到你在评论中尝试了各种各样的东西,但是当你尝试它们时,不清楚你是否评论了findVisualParent
。因此,我不能确切地告诉你哪里出了问题,但可以告诉你,你试图从一个线程访问一个UI组件,而不是通过它的调度程序创建的。
你需要跟踪你的代码,找出到底是哪一行出错了,看看你在哪个线程上,并验证你正在使用正确的调度程序。
如果你使用这种方法,你应该先使用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);
}
}
我只是将UI调度程序保存在全局位置,当我想从线程切换时,我这样调用:
ChangetoWindow() {
dispatcher.Invoke(() => navigationService。导航(新窗口()));}