質問

I have an application written in VB.net. In that application I have multiple forms and lots of functionality. The form the application starts with, is some kind of menu. In the background, I have a list of menu items that a user can see and use to open a new form. It is possible to search through all those menu items via a textbox where you can fill in some text and the code then compares all the menu items names to the filled in text and shows the result. This event is fired on every textchanged event of this textbox. But if the user types in a name that occures a lot (like à 100 times or so) the view takes some time (3 to 5 sec) to display all those results. Now I would like to know if it is possible to abort the first event handler if the same event is called again. That means that if I am typing in the textbox and for the first 4 or 5 letters almost all menu items are matches, so I want to abort that search and start a new one right away. Is there any way to detect that the same event is called again and abort the currect one to make the new one start right away?

Thanks in advance for reading this and helping me solve this problem!

役に立ちましたか?

解決

In order to accomplish this, you are going to have to do the work in a separate thread. The primary reason for that is that WinForms are single-threaded. All UI-related events in a WinForm are handled on the same UI thread. As such, there is no way for the TextChanged event to fire again while you are still in the middle of processing the previous event. The UI will be locked up until the first event if finished processing.

However, if you do all of the menu-filtering work in another thread, then your UI will be freed-up to react to user input while you are doing the work. Then your TextChanged event will be allowed to fire before the previous one is done processing.

The easiest way to implement multi-threading in a WinForm project is to use the BackgroundWorker component. You can find it in the form-designer tool box. Luckily, the BackgroundWorker component has some properties and methods which are useful for implementing the cancellation as you described.

For instance, here's a very simple example. In this example, every time the text in TextBox is changed, it starts BackgroundWorker1 performing some work. The work that it does is to simply wait two seconds and then copy the contents of TextBox1 to TextBox2. If the text changes again before those two seconds are complete, it cancels the bacground work and starts it again from the beginning.

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

In order for the above example to work, the BackgroundWorker1.WorkerSupportsCancellation property must be set to True.

As you can see, when the text changes, it simply checks the IsBusy property, which determines whether or not the background thread is still working from a previous event. If it is, it cancels it. If it's not, it starts it.

All of the work which needs to be done on the separate thread is done inside the background worker's DoWork event handler. As it is doing the work, it needs to periodically check whether or not it has been canceled. If it has been canceled, it needs to stop what it's doing and set the Cancel property of the event args to indicate that it is stopping because it was canceled.

Once the background work is done, (whether by cancellation or by completing its task), the background worker raises the RunWorkerCompleted event. The event args for that event have a Cancelled property which indicates whether or not the work completed because it had been canceled prematurely. In the example, if it was canceled, it simply restarts the work from the beginning.

For what it's worth, all of this would be moot if there was some way for you to speed up the menu-filtering algorithm to the point where it's near instantaneous. It may be possible to do that by indexing your menus in something like a suffix array.

他のヒント

Try to add a condition. In your search method, if your text length exceeds some value, call the same event again (like in recursive methods). It should works.

Regards,

Daniel

add some condition on length of search string...like on its Length should be 5 or more

OR maximum results shown at one time should be limited.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top