C# 使用另一个线程在WPF中创建新窗口时的奇怪行为

C# 使用另一个线程在WPF中创建新窗口时的奇怪行为,c#,wpf,multithreading,C#,Wpf,Multithreading,我试图在WPF中创建一个简单的应用程序,它将在一个线程中打开一个新窗口,因为它的行为异常 ArrayList formArray = new ArrayList(); Thread th; Window1 vd; public void Start() { vd = new Window1(); formArray.Add(vd); vd.ShowDialog(); } public void StartCall() { th = new Thread(ne

我试图在WPF中创建一个简单的应用程序,它将在一个线程中打开一个新窗口,因为它的行为异常

ArrayList formArray = new ArrayList();
Thread th;
Window1 vd;

public void Start()
{
    vd = new Window1();

    formArray.Add(vd);
    vd.ShowDialog();
}

public void StartCall()
{
    th = new Thread(new ThreadStart(Start));
    th.SetApartmentState(ApartmentState.STA);
    th.Start();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    StartCall();
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    ((Window1)(formArray[0])).Show();
}
Window1代码是

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    e.Cancel = true;
    this.Hide();
}
当再次尝试打开它时,它只会抛出一个错误
,调用线程无法访问该对象,因为另一个线程拥有它

尝试使用dispatcher时。。援引所有这些都没用。 更奇怪的是,同样的代码在Windows窗体应用程序中工作

也许和这条线有关<代码>th.SetApartmentState(ApartmentState.STA)

可能是这个家伙,但如果我不添加它,它也会失败,错误如下:

附加信息:调用线程必须是STA,因为许多UI组件都需要它。
编辑: 在线程上添加了强制在调度程序上运行。 我还添加了一个显示方法,根据调用的调度器显示对话框。希望对你有所帮助

此外,如本文所述:

完成后,应该关闭相应线程的调度程序

主窗口:

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        StartCall();
    }

    ArrayList formArray = new ArrayList();
    Window1 vd;
    Thread th;

    public void Start()
    {
        vd = new Window1();
        formArray.Add(vd);
        vd.ShowDialog();

        System.Windows.Threading.Dispatcher.Run(); //ok this is magic
    }

    public void StartCall()
    {
        th = new Thread(new ThreadStart(Start));
        th.SetApartmentState(ApartmentState.STA);
        th.Start();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        StartCall();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ((Window1)(formArray[0])).Display();            
    }
窗口1:

    void Window1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        e.Cancel = true;
        this.Hide();
    }

    public void Display()
    {
        if (!Dispatcher.CheckAccess())
        {
            Dispatcher.BeginInvoke((Action)Display);
            return;
        }

        this.Show();
    }

您不能从创建窗口的线程以外的线程调用窗口上的
.Show
(这基本上就是错误消息告诉您的!)。幸运的是,正如您所建议的,这就是dispatcher的作用:将调用封送到正确的线程。但是你必须使用正确的调度器来完成任务

WPF中的每个控件(包括一个窗口)都有一个
.Dispatcher
属性,用于获取该控件线程的调度程序。我猜您在尝试重新打开对话框时使用了主窗口中的对话框,这是错误的。相反,如果您在
按钮中使用此选项,单击
您将获得更多的运气:

var window = (Window1)formArray[0];
window.Dispatcher.Invoke(window.Show);  // note: use the dispatcher that belongs to the window you're calling

(注意:这并不是说这是一种典型的有用/推荐的设计模式。事实上,它通常会导致比它解决的问题更多的问题。但是,这当然是你可以选择做的。)

但是我告诉你,在windows窗体中,同样的代码是有效的@Sheridan你想达到什么目标?在另一个线程上打开窗口是很少见的。@CharlesMager我正在处理一个c#network程序bro@Sheridan如果背景线程是创建窗口的线程,则可以选择。这里正好是这样。不要创建多个UI线程。使用单个UI线程,并在该线程中完成所有UI工作。在非UI线程中执行任何长时间运行的非UI工作,而不是在UI线程中执行非UI工作并创建第二个线程来执行UI工作。你让事情变得比你需要的更艰难了。即使没有主人,这也会起作用。。我问题的重点是如何使用线程,因为我在套接字程序中使用它…@Eledra nguyen您不需要使用
测试
;您只需调用Dispatcher.BeginInvoke(Display)
就可以了。@Dan:BeginInvoke需要一个代理。啊,是的<代码>调用
将接受
操作
,但
开始调用
不会。您可以这样做:
Dispatcher.BeginInvoke((操作)显示)太棒了!根据你的建议更改了答案。选民会解释他们自己吗?我相信我已经发布了一个解决当前问题的有效答案(同时也解释了这通常是一个坏主意)。ty bro我真的很感谢你的帮助我尝试了你的建议,当我触发按钮时,点击ui就冻结了。。。是hangs bro…@user3548681:你在调试它吗?它停在哪条线上?冻结可能意味着你遇到了与线程使用相关的另一个问题…@Dan:你的答案大体上是准确的。但是,在他的特定情况下,他没有初始化Dispatcher循环,导致调用挂起应用程序。@EledraNguyen:“初始化Dispatcher循环”是什么意思