从另一个线程VB.net在主线程上打开窗体



我有一个在我们的软件(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

如果要将方法调用封送到窗体中的UI线程,通常会调用窗体上的Invoke或其控件之一,以调用拥有它的线程上的方法。在这种情况下,这不是一个选项,因为窗体不归UI线程所有。

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

不同之处在于,您无法在表单本身中获得SynchronizationContext.Current,因为它是在辅助线程上创建的。您需要首先在调用表单中,在UI线程上执行的代码中获得SynchronizationContext.Current。然后,当您在辅助线程上执行代码以创建和显示第二个表单时,您会传入该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,而三级形式将需要1个Button

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

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

这让您有了一个开始的想法:(但我要说的是,最好不要只是问,而是展示你想做什么——编码说话——或者通过几行代码给出你想法的小场景(

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

最新更新