WPF 新窗口创建新线程错误

本文关键字:错误 线程 新线程 窗口 新窗口 WPF 创建 | 更新日期: 2023-09-27 17:56:30

void itemCommand_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
{
    var thread = new Thread(() =>
    {
    if (LoginCheck())
    {
        ItemWindow itw = new ItemWindow(); 
        //Dispatcher.CurrentDispatcher.Invoke((System.Action)(() =>
        //{
              itw.Show();
              itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
        //}));
        Dispatcher.Run();
     }
     });
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
}

当此函数调用两次时,我不断收到"调用线程无法访问此对象,因为其他线程拥有它"的错误消息。 它在第一次调用时工作正常,在窗口关闭并尝试再次打开后,它失败了。 正如我注释掉"调用"方法时,它也不适用于调度程序。 请帮助我找到解决方案。谢谢。

-----------------编辑

我创建新线程的原因是因为它是一个 Excel 加载项。 我无法从主线程创建窗口,如果我从主线程创建它们,则主线程是与窗口冲突的 excel。
我不明白的是,为什么新线程中的新实例(ItemWindow)与旧线程冲突。

WPF 新窗口创建新线程错误

我在一个新应用程序中创建了一个简单的测试方法,当我单击主窗体上的(唯一)按钮时调用该方法。该方法如下所示:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Thread thread = new Thread(() =>
    {
        Window1 window = new Window1();
        window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
        window.Show();
        System.Windows.Threading.Dispatcher.Run();
    });
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
}

Window1是我做的一个窗口类,上面只有一个TextBlock。我可以根据需要多次单击该按钮,并且它继续打开新窗口而不会出现任何问题(无论我是否先关闭前一个窗口)。

我怀疑问题发生在您没有向我们展示的代码中。您需要非常小心,新线程上的任何内容都不会尝试从主线程访问任何与 UI 相关的内容。在单独线程上运行的 Windows 无法相互通信,除非它们通过另一个线程的调度程序。当从创建对象的线程以外的线程访问调度程序对象的任何方法或属性时,将引发异常。

退一步说,为什么新窗口在自己的线程上很重要?除非新窗口要独占线程,否则它可能会在主线程上运行良好。如果您正在运行一些长时间的阻塞操作,也许该操作应该单独移动到线程而不是整个窗口。我不知道你到底在做什么,但这是值得思考的事情。


编辑:意识到您可能没有在典型的WPF应用程序中运行(看起来您可能在Office插件中),我更新了我的测试以在自己的线程上完全独立地启动窗口。但是,我仍然能够连续启动两个窗口而没有问题。

这是我的新测试。此方法和测试类Window1是我应用程序的全部内容。

[STAThread]
public static int Main(string[] args)
{
    ThreadStart threadFunc = () =>
    {
        Window1 window = new Window1();
        window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
        window.Show();
        System.Windows.Threading.Dispatcher.Run();
    };
    Thread thread = new Thread(threadFunc);
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join();
    thread = new Thread(threadFunc);
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join();
    return 0;
}

因此,您尝试执行的操作似乎没有任何固有的错误,我也没有在您的代码中看到任何明显的问题。我怀疑在显示自定义窗口的某处发生了一些无效的跨线程通信。(要么是这样,要么您遇到了特定于 Office 插件的问题。

您正在尝试将事件处理程序连接到 ItemWindow 之后,它已经可见。

您需要将顺序从以下位置切换:

ItemWindow itw = new ItemWindow(); 
itw.Show();
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };

ItemWindow itw = new ItemWindow(); 
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
itw.Show();

可能的原因是依赖属性。在线程方面,依赖项属性有点挑剔。

即使您没有定义自己的 DepProps,您的窗口仍然会有一些,并且无法摆脱它们。

DepProps有一个明显的缺点:它们是线程绑定的,不能从另一个线程访问。哪个线程拥有所有权限由初始化 DepProps 的线程定义,在您的情况下是第一次调用 new ItemWindow() 。在第一次调用之后,您的线程被设置,您需要该线程来访问您的 DepProps。

对于第一个窗口,这没有问题,但第二个窗口显然有不同的线程。我不知道 DepProps 是如何做到这一点的,但您可以尝试捕获和恢复第一个线程的同步上下文。另一种选择是捕获第一个线程(而不是主线程)的调度程序