0
votes

I am trying replicate auto-complete functionality that you find on the Google homepage. So, when you start typing in the text box you get a drop down list of suggestions and using the up or down arrow key you can cycle through this list. I am pretty much there with this functionality. The code that I have listed below demonstrates the problem.

When you start typing for the first time and a list of suggestions is displayed, pressing the up arrow key while the text box still has focus should cause the last item in list of suggestions to be focused and selected.

In my example below, the second to last item is selected and I cant' seem to fathom why. I have hard coded the suggestions in my example so there is no need to type. Just pressing the up arrow key should demonstrate the issue.

<Window x:Class="MyTestApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525"
        Loaded="Window_Loaded">
    <StackPanel>
        <TextBox x:Name="nameTextBox"
                 Text="hello world!"
                 PreviewKeyDown="nameTextBox_PreviewKeyDown" />
        <ListBox x:Name="suggestionListBox"
                 DisplayMemberPath="Name"
                 SelectionChanged="suggestionListBox_SelectionChanged"
                 PreviewKeyDown="suggestionListBox_PreviewKeyDown" />
        <Label x:Name="output" />
    </StackPanel>
</Window>


using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace MyTestApplication
{
    public partial class MainWindow : Window
    {
        private const int NOT_SELECTED = -1;

        public MainWindow()
        {
            InitializeComponent();

            suggestionListBox.ItemsSource = new[] 
            {
                new { Name = "aaaaaa"},
                new { Name = "bbbbbbbb"},
                new { Name = "cccc"},
                new { Name = "ddddd"},
                new { Name = "eeeeee"},
                new { Name = "fffffffff"}
            };

            output.Content = suggestionListBox.SelectedIndex.ToString();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            nameTextBox.Focus();
        }

        private void nameTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down)
            {
                suggestionListBox.Focus();
            }

            if (e.Key == Key.Up)
            {
                var listBoxItem = (ListBoxItem)suggestionListBox
                        .ItemContainerGenerator
                        .ContainerFromIndex(suggestionListBox.Items.Count - 1);

                var result = listBoxItem.Focus();
            }
        }

        private void suggestionListBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (    (e.Key == Key.Up && suggestionListBox.SelectedIndex == 0)
                ||  (e.Key == Key.Down && suggestionListBox.SelectedIndex == suggestionListBox.Items.Count - 1))
            {
                suggestionListBox.SelectedIndex = NOT_SELECTED;
                nameTextBox.Focus();
            }
        }

        private void suggestionListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            output.Content = suggestionListBox.SelectedIndex.ToString();
        }
    }
}

I have tried using selected index on the list box but when I call the focus method on the list box the selected index is set to zero. This is why I locate the last list box item in the list of suggestions and apply focus to that, but it highlights the one above. Hope this makes sense. To demonstrate the problem just create a new WPF project and copy and paste the above code in the MainWindow.xaml and MainWindow.Xaml.cs and run the application. All shall be revealed.

Any help or indication would be greatly received.

Best of regards

Mohammad.

1
Try putting a breakpoint in your nameTextBox_PreviewKeyDown method. Does it get called only once one a key press?Alex Florescu

1 Answers

1
votes

PreviewKeyDown event is a tunneling event i.e. it tunnels down from parent control to child control and in case you don't want it to tunnel down further, you have to handled the event by setting its handled property of args to true like this e.Handled = true.

So, this should work for you -

private void nameTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
   if (e.Key == Key.Down)
   {
      suggestionListBox.Focus();
   }

   if (e.Key == Key.Up)
   {
      var listBoxItem = (ListBoxItem)suggestionListBox
                        .ItemContainerGenerator
                        .ContainerFromIndex(suggestionListBox.Items.Count - 1);

      suggestionListBox.SelectedIndex = suggestionListBox.Items.Count - 1;
      listBoxItem.Focus();
      e.Handled = true;
   }
}