We have a custom implementation of a multi-select checkbox in our VB.NET application. It largely works just fine, but we recently replaced a regular single-select ComboBox with this control, and it does not support searching while typing.
For example, if the user wants to get to "zygote" they used to be able to start typing the word and it would slowly get closer. Now, as you type, it jumps to the z's, then the y's, then the g's, and so on.
Is it possible to make it behave as it did with a standard ComboBox?
For now, I capture KeyDown and KeyUp so it does not extraneously select an item as they type, but this is not the ideal final solution.
As described in the comments, a Timer can work, but I'll dismiss it (because old and boring :) and I'll make use of a Stopwatch instead:
The Stopwatch is restarted each time a KeyDown event is generated.
The keys pressed are added to a StringBuilder object (to avoid the creation of a multitude of strings). The StringBuilder container is cleared when the time between key presses is greater than a predefined value: here, I've set it to 400ms
, to test or add a configuration option.
► The double StringBuilder.Append()
is there to preserve the default behavior: when keys are pressed with a long delay, it iterates the Items that begin with the same letter (more or less what File Explorer does).
The KeyDown
handler is added in the Form's Sub New()
, here (to a CheckedListBox named checkedList1
). It can be used to handle any ListBox or CheckedListBox in the Form.
Imports System.Diagnostics
Imports System.Text
Sub New()
AddHandler checkedList1.KeyDown, AddressOf listBox_KeyDown
End Sub
Private swLb As New Stopwatch()
Private sbCLb As New StringBuilder()
Private Sub listBox_KeyDown(sender As Object, e As KeyEventArgs)
Dim checkedList = DirectCast(sender, ListBox)
If e.KeyCode < Keys.A Then Return
If swLb.ElapsedMilliseconds > 400 Then
sbCLb.Clear()
sbCLb.Append(ChrW(e.KeyData))
swLb.Restart()
Return
End If
e.SuppressKeyPress = True
sbCLb.Append(ChrW(e.KeyData))
Dim idx = checkedList.FindString(sbCLb.ToString())
checkedList.SelectedIndex = If(idx = ListBox.NoMatches, checkedList.SelectedIndex, idx)
swLb.Restart()
End Sub