WPF Editable ComboBox

Wpf

Wpf Problem Overview


I have a ComboBox and ComboBox.IsEditable property is set to True to have a ComboBox act as both a TextBox and a drop-down list simultaneously. But when the ComboBox is data-bound, entering custom text will not cause a new item to be added to the data-bound collection.

For example, if I enter 'Joe' in a ComboBox that is bound to a list of people, which doesn't contain the value 'Joe', then the value 'Joe' is not going to be added to the drop-down list automatically.

What is the best way to handle this?

Wpf Solutions


Solution 1 - Wpf

Here's a basic MVVM compliant way of getting the behaviour you want:

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <ComboBox Margin="30,5,30,5"
                  IsEditable="True"
                  ItemsSource="{Binding Items}"
                  SelectedItem="{Binding SelectedItem}"
                  Text="{Binding NewItem, UpdateSourceTrigger=LostFocus}"/>
        <TextBox Margin="30,5,30,5" />
    </StackPanel>
</Window>

MainWindow.cs

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private string _selectedItem;

    private ObservableCollection<string> _items = new ObservableCollection<string>()
    {
        "One",
        "Two",
        "Three",
        "Four",
        "Five",
    };

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    public IEnumerable Items
    {
        get { return _items; }
    }

    public string SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            OnPropertyChanged("SelectedItem");
        }
    }

    public string NewItem
    {
        set
        {
            if (SelectedItem != null)
            {
                return;
            }
            if (!string.IsNullOrEmpty(value))
            {
                _items.Add(value);
                SelectedItem = value;
            }
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

I had to place another control in the window as I have set the UpdateSourceTrigger property of the Text binding to LostFocus. If there are no other controls in the window then the Combobox will never lose focus.

I changed the update mode because the default update mode is Propertychanged which will add a new item for each keystroke.

E.G. if you entered the text "Window", the following would be added to your collection:

W
Wi
Win
Wind
Windo
Window

Solution 2 - Wpf

I would handle it in the LostFocus event.

Here you can check if the SelectedItem is null. If so, add the value of Text to the bound list and set SelectedItem to the new item.

In XAML:

  <ComboBox Name="_list" LostFocus="LostFocus" IsEditable="True"/>

In code-behind:

    private ObservableCollection<string> _names;
    public MainWindow()
    {
        InitializeComponent();
        _names = new ObservableCollection<string> {"Eric", "Phillip"};
        _list.SetBinding(ItemsControl.ItemsSourceProperty, new Binding {Source = _names});
    }

    private void LostFocus(object sender, RoutedEventArgs e)
    {
        var comboBox = (ComboBox) sender;
        if(comboBox.SelectedItem != null)
            return;
        var newItem = comboBox.Text;
        _names.Add(newItem);
        comboBox.SelectedItem = newItem;
    }

Hope this helps :)

Solution 3 - Wpf

My suggestion would be using an MVVM-approach and bind your ComboBox.Text to some TextProperty of your ViewModel. (Same can be achieved by just adding a string property to your view) Then you can treat the input in the setter of this property and add that new item to the list, no matter which way it was "committed" in the view. AFAIK there is no out-of-the-box mechanism to add new items to your datasource, you will have to do the item-generation yourself anyway.

Alternatively, you can bind both - SelectedItem and Text of your ComboBox - to avoid lookups in case the user has entered a known item. But that part may be less important to answer your question.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionrs199483View Question on Stackoverflow
Solution 1 - WpfBenjamin GaleView Answer on Stackoverflow
Solution 2 - WpfGoblinView Answer on Stackoverflow
Solution 3 - WpfSimon D.View Answer on Stackoverflow