Search code examples
c#wpfcombobox

WPF, ComboBox, Show candidates on type, but not auto-complete?


Is it possible to make the WPF ComboBox to show candidates below like how Google suggestion works, but NOT auto-complete the rest of the text on the input field?

For example, if I type "b", in the WPF code at the bottom, I get this.

enter image description here

But this causes some problems with Asian language IME's that use text composition. Also, this does not show all other candidates that start with "b". Instead, can I make WPF ComboBox show like this, and let users select the candidate with arrow keys and tab, just like most IDE text editors?

enter image description here

    <ComboBox HorizontalAlignment="Left" Margin="111,61,0,0" VerticalAlignment="Top" Width="120" IsEditable="True">
        <ComboBoxItem Content="Alice"/>
        <ComboBoxItem Content="Bob"/>
        <ComboBoxItem Content="Bart"/>
        <ComboBoxItem Content="Bort"/>
        <ComboBoxItem Content="Charlie"/>
    </ComboBox>

Solution

  • It is possible to implement filtering of items of the ItemsSource collection depending on the entered text.
    But, in my opinion, it is impossible to change the automatic selection of the first suitable one - this behavior is nested in the ComboBox logic.
    And it is impossible to distinguish user input from the text of the first element. Because of this, filtering is also impossible to configure correctly.
    Without changing the ComboBox template or replacing it with your own custom element, in my opinion, it will not be possible to implement this.

    An example of an Expander-based implementation:

    First of all, never populate a ComboBox, ListBox (and other ItemsControl) with a UI Item.
    Create an observable collection and pass it to the ItemsSource.

    Second, get a representation of this collection and set in its Filter property your handler method for filtering elements.
    It should compare the elements of the collection passed to the ItemsSource with the Text value, which specifies the filter template.
    If Text is empty (string.IsNullOrWhiteSpace (Text) = true), then the filter should skip all elements.

    Third, when the Text changes, call Refrech on the collection view.

    <Window x:Class="FilterComboBox.FilterComboBoxWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:FilterComboBox"
            mc:Ignorable="d"
            Title="FilterComboBoxWindow" Height="450" Width="800">
        <Grid x:Name="grid">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Expander>
                <Expander.Header>
                    <TextBox x:Name="tBox" MinWidth="100"
                             TextChanged="OnTextChanged"/>
                </Expander.Header>
                <ListBox x:Name="listBox" SelectionChanged="OnSelectionChanged"/>
                <Expander.Style>
                    <Style TargetType="Expander">
                        <Style.Triggers>
                            <Trigger Property="IsKeyboardFocusWithin" Value="True">
                                <Setter Property="IsExpanded" Value="True"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Expander.Style>
    
            </Expander>
            <TextBlock Grid.Row="1" Text="{Binding SelectedItem, ElementName=listBox}"/>
        </Grid>
    </Window>
    
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace FilterComboBox
    {
        /// <summary>
        /// Логика взаимодействия для FilterComboBoxWindow.xaml
        /// </summary>
        public partial class FilterComboBoxWindow : Window
        {
            private readonly ObservableCollection<string> contetnItems
                = new ObservableCollection<string>()
                {
                    "Alice",
                    "Bob",
                    "Bart",
                    "Bort",
                    "Charlie"
                };
    
            public FilterComboBoxWindow()
            {
                InitializeComponent();
    
                ICollectionView view = CollectionViewSource.GetDefaultView(contetnItems);
                view.Filter = OnFilter;
                listBox.ItemsSource = contetnItems;
            }
    
    
    
            private bool OnFilter(object obj)
            {
                return
                    string.IsNullOrWhiteSpace(tBox.Text) ||
                    ((obj is string text) && text.ToUpper().StartsWith(tBox.Text.ToUpper()));
            }
    
            private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                tBox.Text = listBox.SelectedItem?.ToString();
            }
    
            private void OnTextChanged(object sender, TextChangedEventArgs e)
            {
                CollectionViewSource.GetDefaultView(contetnItems).Refresh();
            }
        }
    }
    

    P.S. Much of this is much more convenient and more correct to do using the MVVM pattern.