也许这是一个简单的问题,我只是不知道正确的搜索条件来找到答案,但我的Google-fu在这个问题上让我失望。
我的vb.net应用程序有一个后台线程来控制所有套接字通信。偶尔,我需要这个通信线程来打开一个模态表单来显示消息并阻止UI交互,直到通信线程完成一系列任务,此时通信线程将删除模态表单,允许用户继续交互。
目前,我的包含后台线程的通信类有两个事件,StartBlockingTask和EndBlockingTask。我的主窗体有这些事件的事件监听器,它们调用类似命名的subs。他们调用的代码如下所示:
Private Delegate Sub BlockingDelegate(ByVal reason As String)
Private Sub StartBlockingTask(ByVal reason As String)
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf StartBlockingTask)
Me.Invoke(del, New Object() {reason})
Else
Try
_frmBlock.lblBlock.Text = reason
_frmBlock.ShowDialog()
Catch ex As Exception
'stuff
End Try
End If
End Sub
Private Sub EndBlockingTask()
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf EndBlockingTask)
Me.Invoke(del, New Object() {""})
Else
Try
If (Not _frmBlock Is Nothing) Then
_frmBlock.DialogResult = Windows.Forms.DialogResult.OK
End If
Catch ex As Exception
'stuff
End Try
End If
End Sub
这成功阻止了UI与交互,但它也阻塞了通信线程,因此EndBlockingTask事件实际上从未被引发。如何从通信线程打开此模式对话框并允许通信线程继续运行?
提前致谢!
答案 0 :(得分:3)
我不同意。
所有需要做的就是将Invoke()更改为 BeginInvoke(),然后你就是金色。
这是因为Invoke()实际上是同步的,导致它阻塞,直到ShowDialog()结算。
使用BeginInvoke()使其成为异步,并允许在线程继续时阻止UI:
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If Not BackgroundWorker1.IsBusy Then
BackgroundWorker1.RunWorkerAsync()
End If
End Sub
Private Delegate Sub BlockingDelegate(ByVal reason As String)
Private Sub StartBlockingTask(ByVal reason As String)
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf StartBlockingTask)
Me.BeginInvoke(del, New Object() {reason})
Else
Try
_frmBlock.lblBlock.Text = reason
_frmBlock.ShowDialog()
Catch ex As Exception
'stuff
End Try
End If
End Sub
Private Sub EndBlockingTask()
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf EndBlockingTask)
Me.BeginInvoke(del, New Object() {""})
Else
Try
If (Not _frmBlock Is Nothing) Then
_frmBlock.DialogResult = Windows.Forms.DialogResult.OK
End If
Catch ex As Exception
'stuff
End Try
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
For i As Integer = 1 To 10
BackgroundWorker1.ReportProgress(i)
System.Threading.Thread.Sleep(1000)
If i = 4 Then
Dim del As New BlockingDelegate(AddressOf StartBlockingTask)
del("bada...")
ElseIf i = 7 Then
Dim del As New BlockingDelegate(AddressOf EndBlockingTask)
del("bing!")
End If
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Label1.Text = e.ProgressPercentage
End Sub
End Class
答案 1 :(得分:1)
您正在创建它的子区域内调用该地址。需要从该子区域外调用该地址。
Private Sub StartBlockingTask(ByVal reason As String)
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf StartBlockingTask)
Private Sub EndBlockingTask()
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf EndBlockingTask)
您需要创建两个委托。一个用于StartBlockingTask,另一个用于EndBlockingTask
这是来自MSDN的一个例子,
Delegate Sub MySubDelegate(ByVal x As Integer)
Protected Sub Test()
Dim c2 As New class2()
' Test the delegate.
c2.DelegateTest()
End Sub
Class class1
Sub Sub1(ByVal x As Integer)
MessageBox.Show("The value of x is: " & CStr(x))
End Sub
End Class
Class class2
Sub DelegateTest()
Dim c1 As Class1
Dim msd As MySubDelegate
c1 = New Class1()
' Create an instance of the delegate.
msd = AddressOf c1.Sub1
msd.Invoke(10) ' Call the method.
End Sub
End Class
http://msdn.microsoft.com/en-us/library/5t38cb9x(v=vs.71).aspx
如果有帮助,请告诉我。