【问题标题】:VB.net event handler abort when it is fired againVB.net 事件处理程序在再次触发时中止
【发布时间】:2014-02-05 15:52:48
【问题描述】:

我有一个用 VB.net 编写的应用程序。在那个应用程序中,我有多种形式和很多功能。应用程序开始的形式是某种菜单。在后台,我有一个用户可以查看并用于打开新表单的菜单项列表。可以通过文本框搜索所有这些菜单项,您可以在其中填写一些文本,然后代码将所有菜单项名称与填写的文本进行比较并显示结果。此文本框的每个 textchanged 事件都会触发此事件。但是,如果用户键入一个经常出现的名称(例如 à 100 次左右),则视图需要一些时间(3 到 5 秒)才能显示所有这些结果。现在我想知道如果再次调用相同的事件,是否可以中止第一个事件处理程序。这意味着如果我在文本框中输入并且前 4 或 5 个字母几乎所有菜单项都匹配,所以我想中止该搜索并立即开始新的搜索。有什么方法可以检测到再次调用相同的事件并中止当前的事件以立即开始新的事件?

提前感谢您阅读本文并帮助我解决此问题!

【问题讨论】:

  • 这完全取决于您如何“搜索”结果。可以发一下代码吗?

标签: vb.net linq events search flowlayoutpanel


【解决方案1】:

为了实现这一点,您将不得不在一个单独的线程中完成这项工作。主要原因是 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 之类的方式中索引您的菜单来做到这一点。

【讨论】:

  • 感谢详细的回复!我认识后台工作人员,但真的没有这样想过。我马上试试。菜单项的搜索和显示尽可能快,但我还是会看看它。我真的很想知道针对这个特定问题的不同解决方案。谢谢!
【解决方案2】:

尝试添加条件。在您的搜索方法中,如果您的文本长度超过某个值,请再次调用相同的事件(如在递归方法中)。它应该可以工作。

问候,

丹尼尔

【讨论】:

  • 那是完全不同的观点,谢谢!我一定会试一试的。
【解决方案3】:

在搜索字符串的长度上添加一些条件...比如它的长度应该是 5 或更多

应该限制一次显示的最大结果。

【讨论】:

  • 开始搜索的最小字符串长度为 3,最大结果可能是可能的,我会试一试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-23
相关资源
最近更新 更多