代码之家  ›  专栏  ›  技术社区  ›  John Christman

如何在自己的线程中打开WPF窗口,然后关闭它?

  •  1
  • John Christman  · 技术社区  · 15 年前

    我试图给用户一些娱乐,并显示一个“请稍候”窗口,与字幕,在加载一个单独的复杂窗口。我试图通过在新线程中加载窗口来完成此操作,如下所示:

    
        Public Function ShowPleaseWait() As System.Threading.Thread
            Dim PleaseWait As New System.Threading.Thread(AddressOf LoadPleaseWait)
            PleaseWait.SetApartmentState(System.Threading.ApartmentState.STA)
            PleaseWait.IsBackground = True
            PleaseWait.Start()
    
            Return PleaseWait
        End Function
    
        Public Sub LoadPleaseWait()
            Dim window As New windowPleaseWait
            Try
                window.Show()
                System.Windows.Threading.Dispatcher.Run()
            Catch e As System.Threading.ThreadAbortException
                window.Close()
                window = Nothing
            End Try
        End Sub 
    

    在调用代码中,它调用 ShowPleaseWait 并保存线程以供以后使用。。要关闭窗口,它会调用 Thread.Abort Catch . 我试过很多不同的方法,不管有没有抓到。

    第一次叫它的时候,它的效果非常好。但是,其他调用将在 window.Show() The calling thread cannot access this object because a different thread owns it. .

    window.Show 而且是本地的。它是如何被其他线程拥有的?我怎样才能解决这个问题?

    3 回复  |  直到 15 年前
        1
  •  1
  •   Ian Griffiths    15 年前

    我已经粘贴了您在这里显示的代码,无法复制该问题(它有一个问题,但并不是您描述的问题的根源。)不幸的是,当我尝试您发布的代码时,我可以调用ShowPleaseWait任意多次,但我没有看到异常。

    肯定发生了两件事中的一件。可能,您已经修改了代码,简化了原始示例,以便在这里发布一些相当小的内容,因此,您也可能已经消除了问题(如果我不得不冒险猜测的话,那就是您的Dim window作为New window它最初是一个类成员而不是本地成员,因此每次都使用相同的window对象。这是我唯一能解释你描述的症状的方法。)

    或者,我使用的代码与您不同。

    为了研究最后一个问题,我将解释我是如何尝试您的代码的,这样您就可以看到是否有任何明显不同于您运行它的上下文的内容。我创建了一个新的WPF应用程序,并将您的方法粘贴到MainWindow.xaml.vb codebhind中。然后,我在窗口中添加了一个按钮,并在click处理程序中调用了您的方法:

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button2.Click
        ShowPleaseWait().Join()
    End Sub
    

    我在返回的线程上调用Join有两个原因。首先,我想阻止主UI线程,以验证辅助窗口是否真的在一个单独的线程上运行(实际上是这样)。第二,我想验证线程是否真的在关闭—通过线程中止来关闭它是一种非常不正常的方式(正确的做法是关闭在该线程上隐式创建的调度程序,这将使对dispatcher.Run()的调用能够干净地退出。这就是我在开头提到的问题。)

    为了保持我的示例与您的示例相似,我通过调用Thread.Abort关闭子窗口,尽管这不是一个好主意-我在子窗口上的按钮的单击处理程序中这样做(我突然想到,只有在从主线程调用子线程上的Abort时,问题才会出现。所以我还添加了几个按钮,让我这样做。没有变化-我仍然可以显示窗口,然后销毁线程,因为我喜欢的次数从来没有看到你所描述的错误。)

    因此,要么是您使用此代码的上下文导致它失败,要么是您在此处发布的代码意外地删除了问题。不管怎样,恐怕还需要更多的信息。

        2
  •  1
  •   Suraj    9 年前

    我甚至没有在你的代码中发现任何问题。 将这些方法放在主模块中。

    Public Function ShowPleaseWait() As System.Threading.Thread
        Dim PleaseWait As New System.Threading.Thread(AddressOf LoadPleaseWait)
        PleaseWait.SetApartmentState(System.Threading.ApartmentState.STA)
        PleaseWait.IsBackground = True
        PleaseWait.Start()
    
        Return PleaseWait
    End Function
    
    Public Sub LoadPleaseWait()
        Dim window As New windowPleaseWait
        Try
            window.Show()
            System.Windows.Threading.Dispatcher.Run()
        Catch e As System.Threading.ThreadAbortException
            window.Close()
            window = Nothing
        End Try
    End Sub
    

    现在在需要显示窗口的地方调用函数,如下所示。

    Dim myThreadWindow as System.Threading.Thread 
    myThreadWindow  = ShowPleaseWait()
    

    myThreadWindow.Abort 
    
        3
  •  0
  •   slugster Joey Cai    15 年前

    UI操作必须在UI线程上完成。这意味着,如果您想从后台线程操作UI内容,那么需要将正在执行的代码/函数封送回UI线程 之前 执行UI操作。

    你的流程应该是这样的:

    • 在空状态下加载主窗口

    • 启动您的数据加载UI体验(在您的情况下,“请稍候”动画)

    • 启动后台线程

    • 使用该后台线程获取和/或操作数据

    • 后台线程完成后,填充ViewModel,或者将代码执行封送回UI线程以填充UI