Фокусировка ListBoxItem в ListBox выбирает ListBoxItem над ним, а не тот, который должен быть выбран

Я пытаюсь воспроизвести функцию автозаполнения, которую вы найдете на главной странице Google. Таким образом, когда вы начинаете вводить текст в текстовое поле, вы получаете раскрывающийся список предложений, и с помощью клавиши со стрелкой вверх или вниз вы можете циклически перемещаться по этому списку. Я в значительной степени там с этой функциональностью. Код, который я перечислил ниже, демонстрирует проблему.

Когда вы начинаете печатать в первый раз и отображается список предложений, нажатие клавиши со стрелкой вверх, когда текстовое поле все еще находится в фокусе, должно привести к тому, что последний элемент в списке предложений будет сфокусирован и выбран.

В моем примере ниже выбран предпоследний элемент, и я не могу понять, почему. Я жестко закодировал предложения в моем примере, поэтому нет необходимости печатать. Простое нажатие клавиши со стрелкой вверх должно продемонстрировать проблему.

<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();
        }
    }
}

Я попытался использовать выбранный индекс в списке, но когда я вызываю метод фокуса в списке, выбранный индекс устанавливается равным нулю. Вот почему я нахожу последний элемент списка в списке предложений и применяю к нему фокус, но он выделяет тот, что выше. Надеюсь, это имеет смысл. Чтобы продемонстрировать проблему, просто создайте новый проект WPF, скопируйте и вставьте приведенный выше код в файлы MainWindow.xaml и MainWindow.Xaml.cs и запустите приложение. Все должно быть раскрыто.

Любая помощь или указание будут приняты.

С наилучшими пожеланиями

Мохаммад.


person dezzy    schedule 04.03.2012    source источник
comment
Попробуйте поставить точку останова в методе nameTextBox_PreviewKeyDown. Он вызывается только один раз при нажатии клавиши?   -  person Alex Florescu    schedule 04.03.2012


Ответы (1)


Событие PreviewKeyDown является событием tunneling, т. е. оно туннелирует от родительского элемента управления к дочернему, и если вы не хотите, чтобы он туннелировал дальше, вы должны обработать событие, установив для его обработанного свойства args значение true, как это e.Handled = true.

Итак, это должно сработать для вас -

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;
   }
}
person Rohit Vats    schedule 04.03.2012