从不同的线程 VB.net 在主线程上打开表单

时间:2021-05-12 15:57:10

标签: .net vb.net winforms

我有一个在我们的软件 (VB.NET) 中运行的自定义通知。通知会在新线程中打开,因此当通知淡入或淡出时,用户仍然可以与软件进行交互。

我希望用户能够点击通知并在主线程上打开一个表单。当我使用普通的 Form.Show() 方法时,它会在与通知相同的线程上打开,然后在通知关闭时关闭。

如何让不同的线程在主线程上执行子操作?

我目前的代码。

这开始通知

Public Sub StartNotificationThread(sender, e) 'started from main thread
    Try
        thread2 = New System.Threading.Thread(AddressOf Notificaition)'my notification thread

        thread2.SetApartmentState(ApartmentState.STA)
        thread2.Start()
    Catch ex As Exception
        error_form = Me.ToString
        Email_Error(sender, ex)
    End Try
End Sub

我想点击我的通知并在主线程上做这样的事情

Public Sub NotificationClicked()'launched from notification thread
    Form1.show 'Do stuff in main thread
    Me.Close()
End Sub

2 个答案:

答案 0 :(得分:1)

如果您想将方法调用编组到表单中的 UI 线程,您通常会在表单或其控件之一上调用 Invoke 以调用拥有它的线程上的方法。在这种情况下,这不是一个选项,因为表单不属于 UI 线程。

在除表单之外的类中,您通常使用 SynchronizationContext 类,在创建对象时获取 Current 值,然后在辅助线程上执行的代码中使用它。这就是你应该在这里做的,但有一个变化。

不同之处在于您无法在表单本身中获取 SynchronizationContext.Current,因为它是在辅助线程上创建的。您需要首先在调用表单中获取 SynchronizationContext.Current,在 UI 线程上执行的代码中。然后,当您在辅助线程上执行代码以创建和显示第二个表单时,您传入该 SynchornizationContext 实例,然后使用它在 UI 线程上执行代码。这是一个简单的例子:

主要(启动)形式:

Imports System.Threading

Public Class Form1

    'The SynchronizationContext for the UI thread.
    Private uiContext As SynchronizationContext = SynchronizationContext.Current

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'Display a new Form2 instance on a secondary thread.

        Call New Thread(Sub() Call New Form2(uiContext).ShowDialog()).Start()
    End Sub

End Class

二级表格:

Imports System.Threading

Public Class Form2

    'The SynchronizationContext for the UI thread.
    Private uiContext As SynchronizationContext

    Public Sub New(uiContext As SynchronizationContext)
        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

        Me.uiContext = uiContext
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'Display text on the default instance of Form1.
        'This text will not be seen because the open Form1 instance is not owned by the current thread.
        Form1.Label1.Text = Date.Now.ToString()
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        'Display a new Form3 instance on the UI thread.
        uiContext.Send(Sub(state) Call New Form3().Show(), Nothing)

        Close()
    End Sub

End Class

三级形式:

Public Class Form3

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'Display text on the default instance of Form1.
        'This text will be seen because the open Form1 instance is owned by the current thread.
        Form1.Label1.Text = Date.Now.ToString()
    End Sub

End Class

初级形式需要一个 Button 和一个 Label,二级形式需要两个 Buttons,三级形式需要一个 Button

当您单击主窗体上的 Button 时,它会在辅助线程上创建和显示辅助窗体,并将 SynchrinizationContext 传递给 UI 线程。您可以向自己证明辅助表单归辅助线程所有,因为它是通过调用 ShowDialog 显示的,但您仍然可以访问主表单。此外,您可以单击第一个 Button,主窗体上的 Label 将不受影响。这是因为代码在辅助线程上执行,因此该线程的 Form1 的默认实例不是显示的实例。

然后您可以单击二级窗体上的第二个 Button 以在 UI 线程上创建和显示三级窗体并关闭二级窗体。您可以通过单击 Button 并查看主表单更新上的 Label 来向自己证明它归 UI 线程所有。这说明当前线程的默认实例是当前显示的实例,所以当前线程必须是UI线程。

答案 1 :(得分:0)

这给了你一个开始的想法: (但让我这么说,最好不仅要问,还要展示你想做什么 - 编码说话 - 或者通过一些代码行给出你的想法的一个小场景)

   'It’s supposed to be inside the main form
    Sub SubToCall(otherParam As String)
        Debug.WriteLine("SubToCall called " & otherParam)
        Me.ShowDialog()
    End Sub

    Sub CallMeFromEveryWhere()
        If InvokeRequired Then
            Invoke(Sub()
                       SubToCall(" from another thread ")
                   End Sub)
        Else
            SubToCall(" from main thread ")
        End If
    End Sub

然后你可以从每个线程调用如下:

Dim T As Threading.Thread = New Threading.Thread(Sub()
                                                   MainForm.CallMeFromEveryWhere()
                                             End Sub)

T.Start()

并显示您的主线程表单,您可以使用如下:

        Dim T As Threading.Thread = New Threading.Thread(Sub()
                                                         Invoke(Sub()
                                                                    MainThreadForm.Show()
                                                                End Sub)
                                                     End Sub)
        T.Start()
相关问题