如何在没有DoEvents的情况下取消WPF中的BackgroundWorker
我有一个搜索框,在 WinForms 中工作得很好,但在 WPF 中却给我带来了麻烦。
它的工作原理是在每次推送字母时开始搜索,类似于谷歌。
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
If SearchWorker.IsBusy Then
SearchWorker.CancelAsync()
Do While SearchWorker.IsBusy
'Application.DoEvents()
'System.Threading.Thread.Sleep(500)
Loop
End If
doSearchText = txtQuickSearch.Text
SearchWorker.RunWorkerAsync()
End If
每次按下按键时,它都会取消当前的 searchWorker,然后重新启动它。在WinForms中,Do while searchworker.isbusy doevents循环
工作得很好,但由于我无法再访问它,我需要找到一种更好的方法来做到这一点。 Sleep() 使其陷入僵局,我尝试只执行 i+=1 作为打发时间的一种方式,直到它不忙,但这也不起作用......
我应该怎么办?
更新:这是我将其更改为的内容。它有效,但取消部分似乎从未触发,这似乎没有异步运行......
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports System.Threading
Imports System.Threading.Tasks
Public Class QuickSearch
Private doSearchText As String
Private canceled As Boolean
Private curSearch As String
Dim searchResults As New ObservableCollection(Of ocSearchResults)
'Task Factory
Private cts As CancellationTokenSource
Private searchtask As Task(Of ObservableCollection(Of ocSearchResults))
Private Sub txtQuickSearch_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.KeyEventArgs) Handles txtQuickSearch.KeyDown
If e.Key = Key.Enter Then
curSearch = ""
End If
If ((txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter)) And Not txtQuickSearch.Text = curSearch Then
If Not cts Is Nothing Then
cts.Cancel()
ColorChecker.CancelAsync()
Try
' searchtask.Wait()
Catch ex As AggregateException
MsgBox(ex.InnerException.Message)
End Try
cts = New CancellationTokenSource
Else
cts = New CancellationTokenSource
End If
Dim cToken = cts.Token
Me.Height = 400
doSearchText = txtQuickSearch.Text
'This always completes fully before continuing on to tRunWorkerComplete(searchtask.Result) '
searchtask = Task(Of ObservableCollection(Of ocSearchResults)).Factory.StartNew(Function() tPerformSearch(cToken), cToken)
Try
tRunWorkerCompleted(searchtask.Result)
Catch ex As AggregateException
' MsgBox(ex.InnerException.Message)
End Try
Else
If Not cts Is Nothing Then
cts.Cancel()
End If
searchResults.Clear()
End If
End Sub
Function tPerformSearch(ByVal ct As CancellationToken) As ObservableCollection(Of ocSearchResults)
On Error GoTo sError
canceled = False
If curSearch = doSearchText Then
canceled = True
Return Nothing
End If
curSearch = doSearchText
Dim SR As New ObservableCollection(Of ocSearchResults)
Dim t As ocSearchResults
Dim rSelect As New ADODB.Recordset
Dim sSql As String = "SELECT DISTINCT CustomerID, CustomerName, City, State, Zip FROM qrySearchFieldsQuick WHERE "
Dim sWhere As String = "CustomerName Like '" & doSearchText & "%'"
SR.Clear()
With rSelect
.Open(sSql & sWhere & " ORDER BY CustomerName", MyCn, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockReadOnly)
Do While Not .EOF
If ct.IsCancellationRequested Then ' This never shows true, the process always returns a value, as if it wasn't async'
canceled = True
Return Nothing
End If
Do While IsDBNull(.Fields("CustomerID").Value)
.MoveNext()
Loop
t = New ocSearchResults(.Fields!CustomerID.Value, .Fields!CustomerName.Value.ToString.Trim, .Fields!City.Value.ToString.Trim, .Fields!State.Value.ToString.Trim, .Fields!Zip.Value.ToString.Trim)
If Not SR.Contains(t) Then
SR.Add(t)
End If
aMoveNext:
.MoveNext()
Loop
.Close()
End With
Return SR
Exit Function
sError:
MsgBox(ErrorToString, MsgBoxStyle.Exclamation)
End Function
Sub tRunWorkerCompleted(ByVal SR As ObservableCollection(Of ocSearchResults))
If canceled Then
Exit Sub
End If
If cts.IsCancellationRequested Then
Exit Sub
End If
searchResults.Clear()
For Each t As ocSearchResults In SR
searchResults.Add(t)
Next
ColorChecker = New BackgroundWorker
ColorChecker.WorkerReportsProgress = True
ColorChecker.WorkerSupportsCancellation = True
ColorChecker.RunWorkerAsync(searchResults)
lblRecordCount.Text = "(" & searchResults.Count & ") Records"
progBar.Value = 100
Exit Sub
sError:
MsgBox(ErrorToString)
End Sub
I have a search box that works great in WinForms, but is giving me trouble in WPF.
It works by starting a search each time a letter is pushed, similar to Google.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
If SearchWorker.IsBusy Then
SearchWorker.CancelAsync()
Do While SearchWorker.IsBusy
'Application.DoEvents()
'System.Threading.Thread.Sleep(500)
Loop
End If
doSearchText = txtQuickSearch.Text
SearchWorker.RunWorkerAsync()
End If
Every time a key is pushed it cancels the current searchWorker then restarts it. In WinForms the Do while searchworker.isbusy doevents loop
worked great, but since I don't have access to that anymore, I need to figure out a better way to do it. Sleep() deadlocks it, and I've tried just doing i+=1 as a way to pass time until it's not busy, but that doesn't work either...
What should I do?
Update: Here's what I changed it to. It works, but the cancel part doesn't seem to ever trigger, this doesn't seem to be running async...
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports System.Threading
Imports System.Threading.Tasks
Public Class QuickSearch
Private doSearchText As String
Private canceled As Boolean
Private curSearch As String
Dim searchResults As New ObservableCollection(Of ocSearchResults)
'Task Factory
Private cts As CancellationTokenSource
Private searchtask As Task(Of ObservableCollection(Of ocSearchResults))
Private Sub txtQuickSearch_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.KeyEventArgs) Handles txtQuickSearch.KeyDown
If e.Key = Key.Enter Then
curSearch = ""
End If
If ((txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter)) And Not txtQuickSearch.Text = curSearch Then
If Not cts Is Nothing Then
cts.Cancel()
ColorChecker.CancelAsync()
Try
' searchtask.Wait()
Catch ex As AggregateException
MsgBox(ex.InnerException.Message)
End Try
cts = New CancellationTokenSource
Else
cts = New CancellationTokenSource
End If
Dim cToken = cts.Token
Me.Height = 400
doSearchText = txtQuickSearch.Text
'This always completes fully before continuing on to tRunWorkerComplete(searchtask.Result) '
searchtask = Task(Of ObservableCollection(Of ocSearchResults)).Factory.StartNew(Function() tPerformSearch(cToken), cToken)
Try
tRunWorkerCompleted(searchtask.Result)
Catch ex As AggregateException
' MsgBox(ex.InnerException.Message)
End Try
Else
If Not cts Is Nothing Then
cts.Cancel()
End If
searchResults.Clear()
End If
End Sub
Function tPerformSearch(ByVal ct As CancellationToken) As ObservableCollection(Of ocSearchResults)
On Error GoTo sError
canceled = False
If curSearch = doSearchText Then
canceled = True
Return Nothing
End If
curSearch = doSearchText
Dim SR As New ObservableCollection(Of ocSearchResults)
Dim t As ocSearchResults
Dim rSelect As New ADODB.Recordset
Dim sSql As String = "SELECT DISTINCT CustomerID, CustomerName, City, State, Zip FROM qrySearchFieldsQuick WHERE "
Dim sWhere As String = "CustomerName Like '" & doSearchText & "%'"
SR.Clear()
With rSelect
.Open(sSql & sWhere & " ORDER BY CustomerName", MyCn, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockReadOnly)
Do While Not .EOF
If ct.IsCancellationRequested Then ' This never shows true, the process always returns a value, as if it wasn't async'
canceled = True
Return Nothing
End If
Do While IsDBNull(.Fields("CustomerID").Value)
.MoveNext()
Loop
t = New ocSearchResults(.Fields!CustomerID.Value, .Fields!CustomerName.Value.ToString.Trim, .Fields!City.Value.ToString.Trim, .Fields!State.Value.ToString.Trim, .Fields!Zip.Value.ToString.Trim)
If Not SR.Contains(t) Then
SR.Add(t)
End If
aMoveNext:
.MoveNext()
Loop
.Close()
End With
Return SR
Exit Function
sError:
MsgBox(ErrorToString, MsgBoxStyle.Exclamation)
End Function
Sub tRunWorkerCompleted(ByVal SR As ObservableCollection(Of ocSearchResults))
If canceled Then
Exit Sub
End If
If cts.IsCancellationRequested Then
Exit Sub
End If
searchResults.Clear()
For Each t As ocSearchResults In SR
searchResults.Add(t)
Next
ColorChecker = New BackgroundWorker
ColorChecker.WorkerReportsProgress = True
ColorChecker.WorkerSupportsCancellation = True
ColorChecker.RunWorkerAsync(searchResults)
lblRecordCount.Text = "(" & searchResults.Count & ") Records"
progBar.Value = 100
Exit Sub
sError:
MsgBox(ErrorToString)
End Sub
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我对 VB 的了解不够,无法为您提供任何编写良好的示例代码,但如果您使用 .Net 4.0,我建议切换到
System.Threading.Tasks
命名空间,其中有 取消功能。I don't know enough VB to give you any well written sample code, but if you're on .Net 4.0 I suggest switching to the
System.Threading.Tasks
namespace, which has cancellation abilities.我不确定
BackgroundWorker
是否足够灵活,可以为此类后台处理提供优雅的解决方案。我认为我要做的是创建一个专用线程来进行搜索。该线程将使用生产者-消费者模式来接受工作项并处理它们。以下代码是我如何看待此策略的工作原理的粗略草图。您可以调用
SearchAsync
方法来请求新的搜索。该方法接受一个回调,当搜索操作发现某些内容时,该回调将被调用。请注意,如果另一个搜索请求排队,使用者代码(在Run
方法中)将取消其当前搜索操作。结果是消费者只处理最新的请求。这就是优雅发生的地方。现在看看如何从 UI 线程实现逻辑。
请注意,
OnSearchComplete
将在工作线程上执行,因此如果要将结果发布到 UI 控件,则需要从回调中调用Dispatcher.Invoke
。I am not sure a
BackgroundWorker
is flexible enough to provide an elegant solution for this type of background processing anyway. I think what I would do is to create a single dedicated thread for doing the searching. This thread would use the producer-consumer pattern for accepting work items and processing them.The following code is a rough sketch of how I see this strategy working. You would call the
SearchAsync
method to request a new search. That method accepts a callback that gets invoked when and if the search operation found something. Notice that the consumer code (in theRun
method) cancels its current search operation if another search request is queued. The effect is that the consumer only ever processes the latest request.Here is where the elegance happens. Look at how you can implement the logic from the UI thread now.
Note that
OnSearchComplete
will be executed on the worker thread so you will need to callDispatcher.Invoke
from within the callback if you want to publish the results to a UI control.您可以通过执行以下操作(在 C# 中)来模拟 WPF 中的 DoEvents:
Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => {}));
You can simulate a DoEvents in WPF by doing (in C#):
Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => {}));