WPF新窗口创建新线程错误

时间:2015-06-15 12:28:54

标签: c# wpf multithreading excel-addins

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();

}

我不断收到错误消息“调用线程无法访问此对象,因为另一个线程拥有它。”在线“itw.show();”当这个函数调用两次时。它适用于第一次调用,在窗口关闭并尝试再次打开后,它会失败。当我注释掉“Invoke”方法时,它也不适用于Dispatcher。请帮我找到解决方案。 谢谢。

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

我创建新主题的原因是因为它是一个Excel插件。如果我从主线程创建它,我无法从主线程创建窗口,这是与窗口碰撞的excel 我不明白的是,为什么新线程中的新实例(ItemWindow)与旧线程冲突。

3 个答案:

答案 0 :(得分:0)

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

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除非通过另一个线程的调度程序,否则无法相互通信。当从创建该对象的线程以外的线程访问DispatcherObject的任何方法或属性时,抛出您看到的异常。

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

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

这是我的新测试。此方法和测试类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插件特有的问题。)

答案 1 :(得分:0)

您正尝试将事件处理程序连接到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();

答案 2 :(得分:-1)

可能的原因是依赖属性。在涉及线程时,依赖属性有点挑剔。

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

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

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