导航到从线程调用的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
});

我不知道该怎么做才能让它起作用。

导航到从线程调用的WPF窗口

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。导航(新窗口()));}