Shahid Altaf
Shahid Altaf

Reputation: 83

Select listbox item using the keyboard

Could anyone please tell me what i should do programmatically to be able to select an item in a listbox using the keyboard when there are multiple items starting with the same character/s. For eg,

If i want to get the focus on "Once" by typing o,n,c what should i do? Instead of jumping from one item to the other as opposed to the default behaviour.

Upvotes: 3

Views: 5581

Answers (3)

Rajendra Maharjan
Rajendra Maharjan

Reputation: 11

I just modified Abbas code for my case. This is my desktop program and using in one of the form which has ListBox control. This is working great for me. Thanks to Abbas

    private DateTime _lastKeyPress;
    private string _searchString;
    private void lstEmployer_KeyPress(object sender, KeyPressEventArgs e)
    {
        var newDate = DateTime.Now;
        var diff = newDate - _lastKeyPress;

        if (diff.TotalSeconds >= 2)
            _searchString = string.Empty;
        _searchString += e.KeyChar;

        lstEmployer.SelectedIndex = lstEmployer.FindString(_searchString);

        _lastKeyPress = newDate;
        e.Handled = true; //REALLY IMPORTANT TO HAVE THIS
    }

Upvotes: 0

Justin
Justin

Reputation: 86779

You want to use the LBS_SORT list box style. This style cannot be applied after the control has been created and so to add this style you need to override the CreateParams property. Create a class (lets call it SortedListBox) which derives from ListBox and override this property like so

public class MyListBox : ListBox
{
    protected override CreateParams CreateParams
    {
        get
        {
            var returnValue = base.CreateParams;
            returnValue.Style |= 0x2; // Add LBS_SORT
            returnValue.Style ^= 128; // Remove LBS_USETABSTOPS (optional)
            return returnValue;
        }
    }
}

This list box should now both sort the items in your list & support incremental searching (you can't turn the sorting off I'm afraid, if you need to control the sort order then you need to do the incremental searching yourself, as Abbas has suggested)

Update: If you additionally remove the LBS_USETABSTOPS style you even get an edit carret showing what character the incremental search is currently matching on

Upvotes: 3

Abbas
Abbas

Reputation: 14432

Add a KeyPress event handler to the ListBox and track the keys that are pressed. Then compare the complete value that already has been typed to the values from the items in the ListBox. If there's a match, select the item.

Edit:

Here's a solution that I created that works. I also measure the time between keypresses. This way, if the time between to keypresses is more than 1.5 seconds, the search-string is emptied and re-filled with the last search-character. After that it's like I said: find a match and if there's a match, select that item. The two private fields are class-level fields to keep track of the time of the last keypress and the string is to store the search-string.

private DateTime _lastKeyPress;
private string _searchString;

private void ListBox1KeyPress(object sender, KeyPressEventArgs e)
{
    var newDate = DateTime.Now;
    var diff = newDate - _lastKeyPress;

    if (diff.TotalSeconds >= 1.5)
        _searchString = string.Empty;
    _searchString += e.KeyChar;

    for (var i = 0; i < listBox1.Items.Count; i++)
    {
        var item = listBox1.Items[i].ToString();
        if (item.ToLower().StartsWith(_searchString))
        {
            listBox1.SelectedItem = item;
            break;
        }
    }
    _lastKeyPress = newDate;
    e.Handled = true; //REALLY IMPORTANT TO HAVE THIS
}

And here's an example using LinQ to get a match for the searchitem:

private void ListBox1KeyPress(object sender, KeyPressEventArgs e)
{
    var newDate = DateTime.Now;
    var diff = newDate - _lastKeyPress;
    if (diff.TotalSeconds >= 1.5)
        _searchString = string.Empty;
    _searchString += e.KeyChar;

    var found = listBox1.Items.Cast<object>().Select(t => t.ToString()).Where(item => item.ToLower().StartsWith(_searchString)).FirstOrDefault();
    if(!String.IsNullOrEmpty(found))
        listBox1.SelectedItem = found;

    _lastKeyPress = newDate;
    e.Handled = true;
}

Hope this helps! ;)

Edit2:

I don't know if you noticed the comment with the imortance of the e.Handled. By default if you press a key in the ListBox, the control will select the first-found item with that key-character. But it has not the functionality that my code has. So, if you remote the e.Handled line, the code will work but the control will also process the keypress and you don't want that: items will not be correctly selected!

Upvotes: 4

Related Questions