UI 线程上的 .NET 引发事件
.NET raise event on UI thread
我正在使用 SQLDependency class to listen for SQL notifications and the SQLDependency.OnChange 事件在与 UI 线程不同的线程上执行。我的目的是在触发此事件时更新网格视图中的数据,以便 SQL 服务器数据库更改可以立即反映在网格中。
如果我 运行 我的 LoadData
方法将数据重新加载到网格中,我得到一个跨线程错误:
我创建了一个自定义 class 来保存所有 SQL 依赖关系相关的代码,并且在我的应用程序中全局声明了这个 class 的一个实例。我在 class 上有一个事件在 SQLDependency.OnChanged event. This event is then handled on various different forms so their data can be reloaded. However, because the OnChange 内部引发,事件是在另一个线程上引发的,我需要先向 运行 添加逻辑:
Delegate Sub ReloadCallback()
Private Sub LoadOnUI()
If Me.InvokeRequired Then
Dim d As ReloadCallback = New ReloadCallback(AddressOf LoadOnUI)
Me.Invoke(d)
Else
Invoke(New MethodInvoker(Sub() RefreshData()))
End If
End Sub
我宁愿避免复制此代码并将其复制到处理我的自定义事件的每个代码。 This answer 几乎正是我要找的东西,但我不知道如何实现所提供的代码。
按照以下逻辑行,获取事件的调用列表并获取其中一个处理程序的线程,然后在该线程上引发事件。以我有限的经验,我无法自己编写代码来完成这项工作。如有任何帮助,我们将不胜感激。
Objective:在 UI 线程上引发事件,事件处理程序中没有重复的额外代码。
解法:
运行 此代码提前在目标线程上(例如 class 构造函数)。
objSyncContext = SynchronizationContext.Current
从另一个线程调用此方法 RunOnUIThread(AddressOf RefreshData)
以 运行 目标线程上的引用方法。
Delegate Sub CallDelegate()
Private Sub RunOnUIThread(objEvent As CallDelegate)
If objSyncContext Is Nothing Then
objEvent()
Else
objSyncContext.Post(Sub() objEvent(), Nothing)
End If
End Sub
这是一个使用 SynchronizationContext 的简单示例。
请注意,它假定您正在从主 UI 线程创建 class,并且您从构造函数存储当前上下文:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim d As New Duck("Bob")
AddHandler d.Quack, AddressOf duck_Quack
Label1.Text = "Waiting for Quack..."
d.RaiseThreadedQuack(3)
End Sub
Private Sub duck_Quack(source As Duck)
Label1.Text = "Quack received from: " & source.Name
End Sub
End Class
Public Class Duck
Public ReadOnly Property Name() As String
Private sc As WindowsFormsSynchronizationContext
Public Sub New(ByVal name As String)
Me.Name = name
sc = WindowsFormsSynchronizationContext.Current
End Sub
Public Event Quack(ByVal source As Duck)
Public Sub RaiseThreadedQuack(ByVal delayInSeconds As Integer)
Dim T As New Threading.Thread(AddressOf ThreadedQuack)
T.Start(delayInSeconds)
End Sub
Private Sub ThreadedQuack(ByVal O As Object)
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(O).TotalMilliseconds)
sc.Post(AddressOf RaiseUIQuack, Me)
End Sub
Private Sub RaiseUIQuack(ByVal O As Object)
RaiseEvent Quack(O)
End Sub
End Class
我正在使用 SQLDependency class to listen for SQL notifications and the SQLDependency.OnChange 事件在与 UI 线程不同的线程上执行。我的目的是在触发此事件时更新网格视图中的数据,以便 SQL 服务器数据库更改可以立即反映在网格中。
如果我 运行 我的 LoadData
方法将数据重新加载到网格中,我得到一个跨线程错误:
我创建了一个自定义 class 来保存所有 SQL 依赖关系相关的代码,并且在我的应用程序中全局声明了这个 class 的一个实例。我在 class 上有一个事件在 SQLDependency.OnChanged event. This event is then handled on various different forms so their data can be reloaded. However, because the OnChange 内部引发,事件是在另一个线程上引发的,我需要先向 运行 添加逻辑:
Delegate Sub ReloadCallback()
Private Sub LoadOnUI()
If Me.InvokeRequired Then
Dim d As ReloadCallback = New ReloadCallback(AddressOf LoadOnUI)
Me.Invoke(d)
Else
Invoke(New MethodInvoker(Sub() RefreshData()))
End If
End Sub
我宁愿避免复制此代码并将其复制到处理我的自定义事件的每个代码。 This answer 几乎正是我要找的东西,但我不知道如何实现所提供的代码。
按照以下逻辑行,获取事件的调用列表并获取其中一个处理程序的线程,然后在该线程上引发事件。以我有限的经验,我无法自己编写代码来完成这项工作。如有任何帮助,我们将不胜感激。
Objective:在 UI 线程上引发事件,事件处理程序中没有重复的额外代码。
解法: 运行 此代码提前在目标线程上(例如 class 构造函数)。
objSyncContext = SynchronizationContext.Current
从另一个线程调用此方法 RunOnUIThread(AddressOf RefreshData)
以 运行 目标线程上的引用方法。
Delegate Sub CallDelegate()
Private Sub RunOnUIThread(objEvent As CallDelegate)
If objSyncContext Is Nothing Then
objEvent()
Else
objSyncContext.Post(Sub() objEvent(), Nothing)
End If
End Sub
这是一个使用 SynchronizationContext 的简单示例。
请注意,它假定您正在从主 UI 线程创建 class,并且您从构造函数存储当前上下文:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim d As New Duck("Bob")
AddHandler d.Quack, AddressOf duck_Quack
Label1.Text = "Waiting for Quack..."
d.RaiseThreadedQuack(3)
End Sub
Private Sub duck_Quack(source As Duck)
Label1.Text = "Quack received from: " & source.Name
End Sub
End Class
Public Class Duck
Public ReadOnly Property Name() As String
Private sc As WindowsFormsSynchronizationContext
Public Sub New(ByVal name As String)
Me.Name = name
sc = WindowsFormsSynchronizationContext.Current
End Sub
Public Event Quack(ByVal source As Duck)
Public Sub RaiseThreadedQuack(ByVal delayInSeconds As Integer)
Dim T As New Threading.Thread(AddressOf ThreadedQuack)
T.Start(delayInSeconds)
End Sub
Private Sub ThreadedQuack(ByVal O As Object)
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(O).TotalMilliseconds)
sc.Post(AddressOf RaiseUIQuack, Me)
End Sub
Private Sub RaiseUIQuack(ByVal O As Object)
RaiseEvent Quack(O)
End Sub
End Class