I am creating a WPF app containing a ComboBox which shows some data. I want to use the combobox-integrated text seach. But the problem is, if the user searchs for "llo", the list should show all items, containing this text snippet, like "Hallo", "Hello", "Rollo" ... But the search returns no result because the property name of no item starts with "llo". Has somebody an idea how to achieve this?
I am using the MVVM-pattern. The view is binded to a collection of DTOs (property of the viewmodel), in the DTO there are two properties which are relevant for the search.
        <ComboBox 
            ItemsSource="{Binding Path=Agencies}"
            SelectedItem="{Binding Path=SelectedAgency}"
            IsTextSearchEnabled="True"
            DisplayMemberPath="ComboText"
            IsEnabled="{Binding IsReady}"
            IsEditable="True"
            Grid.Column="0"
            Grid.Row="0" 
            IsTextSearchCaseSensitive="False"
            HorizontalAlignment="Stretch">
        </ComboBox>
    public class Agency
    {
        public int AgencyNumber { get; set; }
        public string Title { get; set; }
        public string Name { get; set; }
        public string ContactPerson { get; set; }
        public string ComboText => $"{this.AgencyNumber}\t{this.Name}";
    }
Ginger Ninja | Kelly | Diederik Krols definitely provide a nice all in one solution, but it may be a tad on the heavy side for simple use cases. For example, the derived ComboBox gets a reference to the internal editable textbox. As Diederik points out "We need this to get access to the Selection.". Which may not be a requirement at all. Instead we could simply bind to the Text property.
<ComboBox 
    ItemsSource="{Binding Agencies}"
    SelectedItem="{Binding SelectedAgency}"
    Text="{Binding SearchText}"
    IsTextSearchEnabled="False" 
    DisplayMemberPath="ComboText"
    IsEditable="True" 
    StaysOpenOnEdit="True"
    MinWidth="200" />
Another possible improvement is to expose the filter, so devs could easily change it. Turns out this can all be accomplished from the viewmodel. To keep things interesting I chose to use the Agency's ComboText property for DisplayMemberPath, but its Name property for the custom filter. You could, of course, tweak this however you like.
public class MainViewModel : ViewModelBase
{
    private readonly ObservableCollection<Agency> _agencies;
    public MainViewModel()
    {
        _agencies = GetAgencies();
        Agencies = (CollectionView)new CollectionViewSource { Source = _agencies }.View;
        Agencies.Filter = DropDownFilter;
    }
    #region ComboBox
    public CollectionView Agencies { get; } 
    private Agency selectedAgency;
    public Agency SelectedAgency
    {
        get { return selectedAgency; }
        set
        {
            if (value != null)
            {
                selectedAgency = value;
                OnPropertyChanged();
                SearchText = selectedAgency.ComboText;
            }
        }
    }
    private string searchText;
    public string SearchText
    {
        get { return searchText; }
        set
        {
            if (value != null)
            {
                searchText = value;
                OnPropertyChanged();
                if(searchText != SelectedAgency.ComboText) Agencies.Refresh();
            }
        }
    }
    private bool DropDownFilter(object item)
    {
        var agency = item as Agency;
        if (agency == null) return false;
        // No filter
        if (string.IsNullOrEmpty(SearchText)) return true;
        // Filtered prop here is Name != DisplayMemberPath ComboText
        return agency.Name.ToLower().Contains(SearchText.ToLower());
    }
    #endregion ComboBox
    private static ObservableCollection<Agency> GetAgencies()
    {
        var agencies = new ObservableCollection<Agency>
        {
            new Agency { AgencyNumber = 1, Name = "Foo", Title = "A" },
            new Agency { AgencyNumber = 2, Name = "Bar", Title = "C" },
            new Agency { AgencyNumber = 3, Name = "Elo", Title = "B" },
            new Agency { AgencyNumber = 4, Name = "Baz", Title = "D" },
            new Agency { AgencyNumber = 5, Name = "Hello", Title = "E" },
        };
        return agencies;
    }
}
The main gotchas:
SearchText to be updated accordingly.DisplayMemberPath and our custom filter. So if we would let the filter refresh, the filtered list would be empty (no matches are found) and the selected item would be cleared as well.On a final note, if you specify the ComboBox's ItemTemplate, you'll want to set TextSearch.TextPath instead of DisplayMemberPath.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With