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