[英]Open form on main thread from a different thread 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 线程。
在 forms 以外的类中,您通常使用SynchronizationContext
class,在创建 object 时获取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
时,它将在辅助线程上创建和显示辅助窗体,并将 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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.