Tuesday, December 8, 2009

Silverlight Combobox with keyboard input

Recently I had to create some comboboxes in a silverlight app that contained quite a few items. Scrolling through so many items in the list made it difficult to find what you want. I wanted the combo to respond to keyboard input such that I could press a key and have it jump to the first item in the list that started with that letter. I was kind of surprised not to find this as a standard property of the silverlight combo.

The solution I came up with uses the KeyUp event to select the appropriate item in the list. The trick is, you need to handle the KeyUp event for the Combo as well as the ItemsPanel of the combo. Once you start scrolling or interact with the list, you are interacting with the ItemsPanel of the combo, so you need to handle the events there if you want to allow keyboard input multiple times. The XAML looks like this:

<ComboBox
  Width="50"
  Height="20"
  x:Name="cboSimple"
  KeyUp="cboSimple_KeyUp" >
  <ComboBox.ItemsPanel>
    <ItemsPanelTemplate>
      <StackPanel KeyUp="cboSimple_KeyUp"/>
    </ItemsPanelTemplate>
  </ComboBox.ItemsPanel>
  <ComboBoxItem Content="A" />
  <ComboBoxItem Content="B" />
  <ComboBoxItem Content="C" />
  <ComboBoxItem Content="D" />
  <ComboBoxItem Content="E" />
</ComboBox>


Now when you handle the event you to select the item in the combo. So instead of working with the sender for the event, you will work with the combo by name. LINQ can be used for finding all the items that start with the letter selected on the keyboard.


private void cboSimple_KeyUp(object sender, KeyEventArgs e)
{
  SelectItemInCombo(cboSimple, string.Format("{0}", Convert.ToChar(e.PlatformKeyCode)));
}
private void SelectItemInCombo(ComboBox cBox, string keyLetter)
{
  IEnumerable<object> itemsWithStartcChar =
  cBox.Items.Where(c => ((ComboBoxItem)c).Content.ToString().StartsWith(keyLetter));

  if (itemsWithStartcChar.Count() > 0)
  {
    cBox.SelectedItem = itemsWithStartcChar.First();
  }
}


Notice that first I do a select so I can get a count of the items that start with the letter that was pressed. Without this you get an exception on the First function if the list is empty.

This is also a very simple list. If your combo contains more than simple strings you will need to find the string within the ComboBoxItem that you are sorting by.

5 comments:

  1. Nice blog with cool info, but you should post more often..

    ReplyDelete
  2. Very eficiente, efetive and simple.
    I just added a simple improvement to this idea to get the combobox selection through a continous typed word, modifying a little bit the code.
    I’m using a collection binded to the combo items in all my comboboxes. Then I have a ItemCollection instead of a ComboBoxItem. The collection is composed by a class that overrides ToString().

    private void SelectItemInCombo(ComboBox cbox, string key) {
    string word = getWord(cbox, key);
    var itemChoosed = cbox.Items.Where(c => c.ToString().StartsWith(word, StringComparison.InvariantCultureIgnoreCase));

    if (itemChoosed.Count() > 0)
    cbox.SelectedItem = itemChoosed.First();
    }

    private string getWord(ComboBox cbox, string key) {

    DateTime agora = DateTime.Now;
    long tempoAgora = agora.Ticks;
    if (cbox.Tag != null) {
    string[] parts = cbox.Tag.ToString().Split('|');

    long tempoAntes = long.Parse(parts[1]);

    if (TimeSpan.FromTicks(tempoAgora - tempoAntes).TotalMilliseconds < 500 && parts[0].Length < 32) {

    key = parts[0] + key;
    }
    }

    cbox.Tag = key + "|" + agora.Ticks.ToString();
    return key;

    }

    I hope this can be useful for someone else.

    ReplyDelete
  3. Hi,

    I am currently wanting to build a cbox auto tagging service like http://tagaboard.blogspot.sg/ .Could you give me some advise as I do not know how to do it.. Thanks in advance!

    ReplyDelete
  4. In this code which explained top, when user two times same character do not work! for example , in combobox , items are Alabama, Atlantic, when enter 'a' it choose Alabama didnt choose atlantic when press again a ,,,, how can I add this function?

    ReplyDelete