Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't get focus on dynamically created listbox in C#

Tags:

c#

winforms

I have previously asked a question on how to Selecting dynamically created Listboxs items in C# which was answered. The problem now is that when the listbox gets the focus when popped up! I can't continue typing unless I either select an item or press Esc, which I have explicitly defined to set focus back to my TextBox.

The irony is that I have a condition in my KeyDown event which says if the UP or Down arrow keys are pressed, set the focus on ListBox so that user can choose an item, but don't transfer the focus so that user can continue typing freely.

Just like what we have on Visual Studio, when a user presses a dot, he is not blocked and forced to choose an item form the Intellisense list, but he can continue typing and or at any time use arrow keys UP or Down to select the proper item.

I can't achieve the same functionality using the code below. How can I get this to work as mentioned?

I need to say that using ActiveControl, and transferFocus, using this.Focus() prior to lst.Focus(), disabling and enabling textbox they all didn't work!

private void txtResults_KeyDown(object sender, KeyEventArgs e)
{
   string[] words= ((TextBox)sender).Text.Split(' ');
   string s = sampleWord.Text = words[words.Length - 1];

    if (e.KeyCode == Keys.OemPeriod)
    {
        ShowPopUpList(s);
        lst.Focus();     //This transfers the focus to listbox but then prevents user
                         //from being able to type anymore unless he/she chooses an item!
    }
    else if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)
    {
        lst.Focus();//doesnt work :-/
    }
    else
    {
        lst.Hide();
        txtResults.Focus();
    }
}
like image 481
Hossein Avatar asked Aug 02 '13 19:08

Hossein


3 Answers

This behavior is happening because you are shifting focus to the listbox. Try thinking about it differently and instead of giving focus to the listbox, select the next/previous item in the list when the up or down arrows are pressed.

change your "else if" to the following:

        else if (e.KeyCode == Keys.Down)
        {
            if (lst.SelectedIndex + 1 < lst.Items.Count)
            {
                lst.SelectedIndex += 1; 
            }
            e.Handled = true;
        }
        else if (e.KeyCode == Keys.Up)
        {
            if (lst.SelectedIndex - 1 >= 0)
            {
                lst.SelectedIndex -= 1;
            }
            e.Handled = true;
        }
like image 186
SageMage Avatar answered Oct 23 '22 04:10

SageMage


If you want the user to be able to continue typing in the textbox while still manipulating the selection of the listbox, then you will have to fake it: don't actually set focus to the listbox when you show it. Instead, change the selection of the listbox manually in the KeyDown event for the textbox. Something like this:

private void txtResults_KeyDown(object sender, KeyEventArgs e)
{
    string[] words = ((TextBox)sender).Text.Split(' ');
    string s = sampleWord.Text = words[words.Length - 1];

    if (e.KeyCode == Keys.OemPeriod)
    {
        ShowPopUpList(s);
        lst.SelectedIndex = 0;
    }
    else if (lst.Visible && e.KeyCode == Keys.Up)
    {
        // manipulate the selection on the listbox (move up)
        if (lst.SelectedIndex > 0) 
            lst.SelectedIndex -= 1;

        // eat the keypress so it textbox doesn't get it and move the cursor
        e.Handled = true; 
    }
    else if (lst.Visible && e.KeyCode == Keys.Down)
    {
        // manipulate the selection on the listbox (move down)
        if (lst.SelectedIndex < lst.Items.Count - 1) 
            lst.SelectedIndex += 1;

        // eat the keypress so it textbox doesn't get it and move the cursor
        e.Handled = true;
    }
    else if (lst.Visible && e.KeyCode == Keys.Enter)
    {
        // insert current list box selection into text box and hide the list
        txtResults.Text += lst.SelectedItem;
        txtResults.SelectionStart = txtResults.Text.Length;
        lst.Hide();
        // eat the keypress to prevent the textbox (and the form) from acting on it
        e.SuppressKeyPress = true;
        e.Handled = true;
    }
    else
    {
        lst.Hide();
    }
}
like image 2
Brian Rogers Avatar answered Oct 23 '22 06:10

Brian Rogers


You don't need to focus your ListBox to allow your user to select its item using Arrow keys, your TextBox should always be focused, You have to process the Enter right in the KeyDown event handler of your TextBox, add the selected item there not in the KeyDown event handler of your ListBox, something like this:

private void txtResults_KeyDown(object sender, KeyEventArgs e)
{
  string[] words= ((TextBox)sender).Text.Split(' ');
  string s = sampleWord.Text = words[words.Length - 1];

  if (e.KeyCode == Keys.OemPeriod)
  {
    ShowPopUpList(s);//Show your ListBox without needing to focus it.        
  }

  if(lst.Visible){
    if(e.KeyCode == Keys.Down){      
      lst.SelectedIndex = (lst.SelectedIndex + 1) % lst.Items.Count;
    }
    else if (e.KeyCode == Keys.Up){
      lst.SelectedIndex = lst.SelectedIndex == 0 ? lst.Items.Count - 1 : lst.SelectedIndex - 1
    }
    else if (e.KeyCode == Keys.Enter){//Add the selected Item
        //Add the selected Item here not in the KeyDown event handler of your ListBox
        //.........
        //.........
        //Your TextBox may not need to handle the Enter, so just suppress it after adding the selected item
        e.Handled = true;
    }
    else
    {
      lst.Hide();
    }
  }
}
like image 2
King King Avatar answered Oct 23 '22 05:10

King King