Выберите элементы в списке WPF с помощью поиска клавиатуры «Type-Ahead» - PullRequest
3 голосов
/ 18 ноября 2010

У меня есть элемент управления WPF Listbox, и я хотел бы разрешить пользователю изменять выбранный элемент с помощью опережающего ввода.Поведение, которое я ищу, в точности похоже на Windows Explorer.Если вы продолжите вводить текст имени папки, список будет продолжать выбирать более правильный элемент.

Например, предположим, что структура папок такая:

OtherFolderName
MyFirstFolder
MyFirstFileFolder
MyFirstList

Если вы выберете OtherFolderNameс помощью мыши, затем начните вводить MyFirstF, будет выбран элемент MyFirstFolder, но если вы продолжите вводить MyFirstFi, будет выбран элемент MyFirstFileFolder.

Мой WPF Listbox не демонстрирует это поведение, я надеюсь, что я могу легко включить его, так как старый список WinForms сделал именно это.

Ответы [ 2 ]

7 голосов
/ 18 ноября 2010

Взгляните на класс TextSearch, в частности на присоединенное свойство TextSearch.TextPath:

<ListBox TextSearch.TextPath="FolderName" ... />

Свойство TextSearch.TextPath включает текстовый поиск и определяет, как извлечь текст поиска из каждого элемента. В этом случае я предположил, что у каждого из ваших объектов папок есть свойство с именем «FolderName».

Если это не делает все, что вы ищете, вам, вероятно, придется реализовать собственный поиск, так как функция TextSearch не особенно поддается настройке. Для этого:

  1. Обработка события TextInput
  2. Сравните время текущего TextInput с предыдущим TextInput. Если достаточно близко друг к другу, добавьте строку префикса, в противном случае установите для нее введенный одиночный символ.
  3. Поиск всех элементов по заданному префиксу и, если найден, установлен SelectedItem.

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

0 голосов
/ 19 января 2017

Я использую скрытый TextBox, который ненадолго появляется во время набора текста, сбрасывается через пару секунд и очищается, чтобы он не пытался сопоставить свое содержимое после истечения таймера.Человек наберет ListBox, и его событие KeyUp заполнит TextBox из-за привязки к SearchText.Когда SearchText заполнен, он запускает MyFilteredItems(), чтобы выполнить сопоставление между этим текстом и ListBox.Затем, если человек нажмет Enter, выделение перейдет в другой TextBox (не указанный в XAML, но предоставляется как закомментированный в коде) и будет очищен от lstPickList.Затем TextBox очищается и таймер сбрасывается.

XAML:

<TextBox Name="txtPicker" IsReadOnly="True" Foreground="LightGreen" FontFamily="Consolas" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"></TextBox>

<ListBox Name="lstPickList" Grid.Row="1" ItemsSource="{Binding MyFilteredItems}" KeyUp="lstPickList_KeyUp"></ListBox>

И затем это соответствующий код:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private Timer t = new Timer();    
    public System.Windows.Threading.DispatcherTimer tCleanup =
         new System.Windows.Threading.DispatcherTimer();

    private string _searchText; 
    public string SearchText
    {
        get { return _searchText; }
        set
        {
            _searchText = value;

            OnPropertyChanged("SearchText");
            OnPropertyChanged("MyFilteredItems");
        }
    }

    public List<string> MyItems { get; set; }        

    public IEnumerable<string> MyFilteredItems
    {
        get
        {
            if (SearchText == null) return MyItems;

            return MyItems.Where(x => x.ToUpper().StartsWith(SearchText.ToUpper()));
        }            
    }


    public MainWindow()
    {
        InitializeComponent();

        MyItems = new List<string>() { "ABC", "DEF", "GHI" };                      
        this.DataContext = this;

        t.Interval = 1000;
        t.Elapsed += new ElapsedEventHandler(timerCounter);
        tCleanup.Interval = new TimeSpan(0,0,1);
        tCleanup.Tick += new EventHandler(cleanupCounter_Tick);        
        txtPicker.Visibility = Visibility.Collapsed;
        tCleanup.Start();
    }
    private static int counter = 0;
    protected void timerCounter(object sender, ElaspedEventArgs e)
    {
        counter++;   
    }

   protected void cleanupCounter_Tick(object sender, EventArgs e)
   {
        if (counter > 2 && txtPicker.Visibility == Visibility.Visible)
            txtPicker.Visibility = Visibility.Collapsed;   
   }

   private void lstPickList_KeyUp(object sender, KeyEventArgs e)
   {
       ListBox lst = (ListBox)sender;
       string strg = Convert.ToString(e.Key.ToString().Replace("D",""));
       if (counter < 2)
       {
           txtPicker.Visibility = Visibility.Visible;
           t.Start();
           if (strg == "Return")
           {
                txtPicker.Text += "{Enter}";
                SearchText += "{Enter}";
           }
           else
           {
               txtPicker.Text += strg;
               SearchText += strg;
           }
      }
      else
      {
          SearchText = strg;
          txtPicker.Text = strg;
          t.Stop();
          counter = 0;
          t.Start();
       }

       if (strg == "Return")
       {
           // This next line would be if you had a "selected items" ListBox to store the item
           // lstSelectedList.Items.Add(lstPickList.SelectedItem);
           lstPickList.Items.Remove(lstPickList.SelectedItem);
           t.Stop();
           txtPicker.Visibility = Visibility.Collapsed;
           counter = 0;
           txtPicker.Text = String.Empty;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}
...