Dynamic filter of WPF combobox based on text input

前端 未结 6 1632
陌清茗
陌清茗 2020-11-30 11:38

I cant seem to find a direct method for implementing filtering of text input into a list of items in a WPF combobox.
By setting IsTextSearchEnabled to true, the comboBox

6条回答
  •  孤城傲影
    2020-11-30 12:18

    Kelly's answer is great. However, there is a small bug that if you select an item in the list (highlighting the input text) then press BackSpace, the input text will revert to the selected item and the SelectedItem property of the ComboBox is still the item you selected previously.

    Below is the code to fix the bug and add the ability to automatically select the item when the input text matches it.

    using System.Collections;
    using System.Diagnostics;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Input;
    
    namespace MyControls
    {
        public class FilteredComboBox : ComboBox
        {
            private string oldFilter = string.Empty;
    
            private string currentFilter = string.Empty;
    
            protected TextBox EditableTextBox => GetTemplateChild("PART_EditableTextBox") as TextBox;
    
    
            protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
            {
                if (newValue != null)
                {
                    var view = CollectionViewSource.GetDefaultView(newValue);
                    view.Filter += FilterItem;
                }
    
                if (oldValue != null)
                {
                    var view = CollectionViewSource.GetDefaultView(oldValue);
                    if (view != null) view.Filter -= FilterItem;
                }
    
                base.OnItemsSourceChanged(oldValue, newValue);
            }
    
            protected override void OnPreviewKeyDown(KeyEventArgs e)
            {
                switch (e.Key)
                {
                    case Key.Tab:
                    case Key.Enter:
                        IsDropDownOpen = false;
                        break;
                    case Key.Escape:
                        IsDropDownOpen = false;
                        SelectedIndex = -1;
                        Text = currentFilter;
                        break;
                    default:
                        if (e.Key == Key.Down) IsDropDownOpen = true;
    
                        base.OnPreviewKeyDown(e);
                        break;
                }
    
                // Cache text
                oldFilter = Text;
            }
    
            protected override void OnKeyUp(KeyEventArgs e)
            {
                switch (e.Key)
                {
                    case Key.Up:
                    case Key.Down:
                        break;
                    case Key.Tab:
                    case Key.Enter:
    
                        ClearFilter();
                        break;
                    default:                                        
                        if (Text != oldFilter)
                        {
                            var temp = Text;
                            RefreshFilter(); //RefreshFilter will change Text property
                            Text = temp;
    
                            if (SelectedIndex != -1 && Text != Items[SelectedIndex].ToString())
                            {
                                SelectedIndex = -1; //Clear selection. This line will also clear Text property
                                Text = temp;
                            }
    
    
                            IsDropDownOpen = true;
    
                            EditableTextBox.SelectionStart = int.MaxValue;
                        }
    
                        //automatically select the item when the input text matches it
                        for (int i = 0; i < Items.Count; i++)
                        {
                            if (Text == Items[i].ToString())
                                SelectedIndex = i;
                        }
    
                        base.OnKeyUp(e);                    
                        currentFilter = Text;                    
                        break;
                }
            }
    
            protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
            {
                ClearFilter();
                var temp = SelectedIndex;
                SelectedIndex = -1;
                Text = string.Empty;
                SelectedIndex = temp;
                base.OnPreviewLostKeyboardFocus(e);
            }
    
            private void RefreshFilter()
            {
                if (ItemsSource == null) return;
    
                var view = CollectionViewSource.GetDefaultView(ItemsSource);
                view.Refresh();
            }
    
            private void ClearFilter()
            {
                currentFilter = string.Empty;
                RefreshFilter();
            }
    
            private bool FilterItem(object value)
            {
                if (value == null) return false;
                if (Text.Length == 0) return true;
    
                return value.ToString().ToLower().Contains(Text.ToLower());
            }
        }
    }
    

提交回复
热议问题