Dave Johnson
Dave Johnson

Reputation: 825

Change selection in CheckedListBox as user types

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.

Upvotes: 2

Views: 151

Answers (1)

Jimi
Jimi

Reputation: 32288

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

Upvotes: 1

Related Questions