【问题标题】:Select items in WPF Listbox via Keyboard "Type-Ahead" search通过键盘“提前输入”搜索在 WPF 列表框中选择项目
【发布时间】:2017-01-19 06:20:16
【问题描述】:

我有一个 WPF 列表框控件,我希望允许用户通过使用预先输入来更改所选项目。我正在寻找的行为与 Windows 资源管理器完全一样。当您继续键入文件夹名称的文本时,列表将继续选择更正确的项目。

例如假设这个文件夹结构:

OtherFolderName
MyFirstFolder
MyFirstFileFolder
MyFirstList

如果您用鼠标选择OtherFolderName,然后开始输入MyFirstF,将选择项目MyFirstFolder,但如果您继续输入MyFirstFi,则将选择项目MyFirstFileFolder

我的 WPF 列表框没有表现出这种行为,我希望我可以轻松启用它,因为旧的 WinForms 列表框就是这样做的。

【问题讨论】:

    标签: wpf listbox selection typeahead


    【解决方案1】:

    看一下 TextSearch 类,特别是 TextSearch.TextPath 附加属性:

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

    TextSearch.TextPath 属性启用文本搜索并指定如何从每个项目中提取搜索文本。在这种情况下,我假设您的每个文件夹对象都有一个名为“FolderName”的属性。

    如果这不能满足您的所有需求,您可能必须实现自己的搜索,因为 TextSearch 功能不是特别可调整的。为此:

    1. 处理 TextInput 事件
    2. 比较当前TextInput 与之前TextInput 的时间。如果足够接近,则附加到前缀字符串,否则将其设置为输入的单个字符。
    3. 搜索给定前缀的所有项目,如果找到则设置 SelectedItem。

    我将使用附加属性将其构建为一个单独的类,类似于内置的 TextSearch 类。

    【讨论】:

    • 这正是我想要的。谢谢。
    【解决方案2】:

    我使用了一个隐藏的文本框,它会在用户键入时短暂出现,并在几秒钟后重置并清除,这样它就不会在计时器到期后尝试匹配其内容。该人将在 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));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-13
      • 2017-05-21
      • 1970-01-01
      • 1970-01-01
      • 2012-04-28
      • 2020-01-01
      • 1970-01-01
      • 2011-08-05
      相关资源
      最近更新 更多