为了实现这一点,您将不得不在一个单独的线程中完成这项工作。主要原因是 WinForms 是单线程的。 WinForm 中所有与 UI 相关的事件都在同一个 UI 线程上处理。因此,当您仍在处理前一个事件时,TextChanged 事件无法再次触发。如果完成处理,UI 将被锁定直到第一个事件。
但是,如果您在另一个线程中完成所有菜单过滤工作,那么您的 UI 将被释放,以便在您完成工作时对用户输入做出反应。然后您的TextChanged 事件将被允许在前一个事件处理完成之前触发。
在 WinForm 项目中实现多线程的最简单方法是使用BackgroundWorker 组件。您可以在表单设计器工具箱中找到它。幸运的是,BackgroundWorker 组件具有一些属性和方法,它们对于实现您所描述的取消很有用。
例如,这是一个非常简单的示例。在此示例中,每次更改 TextBox 中的文本时,它都会启动 BackgroundWorker1 执行一些工作。它所做的工作就是等待两秒钟,然后将TextBox1 的内容复制到TextBox2。如果在这两秒钟完成之前文本再次更改,它将取消免费工作并从头开始。
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
If BackgroundWorker1.IsBusy Then
BackgroundWorker1.CancelAsync()
Else
BackgroundWorker1.RunWorkerAsync()
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
For i As Integer = 1 To 20
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
Exit Sub
End If
Thread.Sleep(100)
Next
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If e.Cancelled Then
BackgroundWorker1.RunWorkerAsync()
Else
TextBox2.Text = TextBox1.Text
End If
End Sub
为了使上述示例正常工作,BackgroundWorker1.WorkerSupportsCancellation 属性必须设置为 True。
如您所见,当文本更改时,它只是检查IsBusy 属性,该属性确定后台线程是否仍在处理先前的事件。如果是,它会取消它。如果不是,它会启动它。
所有需要在单独线程上完成的工作都在后台工作人员的DoWork 事件处理程序中完成。在工作的过程中,它需要定期检查它是否已被取消。如果它已被取消,则需要停止正在执行的操作并设置事件参数的Cancel 属性以指示它正在停止,因为它已被取消。
一旦后台工作完成(无论是通过取消还是通过完成其任务),后台工作人员将引发RunWorkerCompleted 事件。该事件的事件参数具有Cancelled 属性,该属性指示工作是否已完成,因为它已被提前取消。在示例中,如果它被取消,它只是从头开始重新开始工作。
对于它的价值,如果有某种方法可以让您将菜单过滤算法加速到接近瞬时的程度,那么所有这些都将毫无意义。可以通过在suffix array 之类的方式中索引您的菜单来做到这一点。