How to Create an Editable Combo Box using WPF

Introduction
 
In this blog, I will be explaining how to manage an editable combobox using the WPF concept of behaviours. The user can edit and add new values to the combobox control and, using behaviours, execute their logic with it.
 

Implementation

 
Let's create on small WPF application with a data grid using MVVM, in order to understand how it works
 
1. Create an XAML with two columns, name and departments, where the 'departments' column has a datatemplate of an editable combobox. The user can edit and add new items to it, as mentioned in XAML.
 
  1. <Grid>  
  2.         <DataGrid x:Name="grid1" Margin="0" AutoGenerateColumns="False" ItemsSource="{Binding MainItems}">  
  3.             <DataGrid.Columns>  
  4.                 <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="Name" IsReadOnly="True"/>  
  5.                 <DataGridTemplateColumn MinWidth="200" Width="Auto" Header="Departments">  
  6.                     <DataGridTemplateColumn.CellTemplate>  
  7.                         <DataTemplate>  
  8.                             <ComboBox IsEditable="True" ItemsSource="{Binding Items}"  
  9.                                           SelectedValue="{Binding Department}"   
  10.                                           Text="{Binding NewDepartment, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"   
  11.                                           StaysOpenOnEdit="False"  IsTextSearchCaseSensitive="False"  
  12.                                           local:EditableComboBoxBehavior.AcceptsEnterKey="True"  
  13.                                           ></ComboBox>  
  14.                         </DataTemplate>  
  15.                     </DataGridTemplateColumn.CellTemplate>  
  16.                 </DataGridTemplateColumn>  
  17.             </DataGrid.Columns>  
  18.         </DataGrid>  
  19.     </Grid>  

Behaviours in WPF 

The idea is to set an attached property on an element so that you can gain access to the element from the class that exposes the attached property. Once that class has access to the element, it can hook events to it and, in response to those events firing, make the element do things it would not normally do. It is a convenient alternative to creating and using subclasses and is very XAML-friendly.
 
2. I have created an editableComboboxBehaviour class and attached it to the combobox control. 
  1. public static class EditableComboBoxBehavior  
  2. {  
  3.     public static readonly DependencyProperty AcceptsEnterKeyProperty =  
  4.         DependencyProperty.RegisterAttached("AcceptsEnterKey"typeof(bool), typeof(EditableComboBoxBehavior), new PropertyMetadata(default(bool), OnAcceptsEnterKey));  
  5.   
  6.     public static void SetAcceptsEnterKey(DependencyObject element, bool value)  
  7.     {  
  8.         element.SetValue(AcceptsEnterKeyProperty, value);  
  9.     }  
  10.   
  11.     public static bool GetAcceptsEnterKey(DependencyObject element)  
  12.     {  
  13.         return (bool)element.GetValue(AcceptsEnterKeyProperty);  
  14.     }  
  15.   
  16.     private static void OnAcceptsEnterKey(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)  
  17.     {  
  18.         var element = (UIElement)dependencyObject;  
  19.   
  20.         if ((bool)eventArgs.NewValue)  
  21.         {  
  22.             element.PreviewKeyDown += KeyDownPreview;  
  23.             element.KeyUp += KeyDown;  
  24.         }  
  25.         else  
  26.         {  
  27.             element.PreviewKeyDown -= KeyDownPreview;  
  28.             element.KeyUp -= KeyDown;  
  29.         }  
  30.     }  
  31.   
  32.     private static void KeyDownPreview(object sender, KeyEventArgs e)  
  33.     {  
  34.         if (e.Key == Key.Enter || e.Key == Key.Return)  
  35.         {  
  36.             
  37.         }  
  38.     }  
  39.   
  40.     private static void KeyDown(object sender, KeyEventArgs e)  
  41.     {  
  42.         if (e.Key == Key.Enter || e.Key == Key.Return)  
  43.         {  
  44.             // your logic  
  45.         }  
  46.         else  
  47.         {  
  48.             var comboBox = (ComboBox)sender;  
  49.             var text = comboBox.Text;  
  50.             comboBox.IsDropDownOpen = false;  
  51.         }  
  52.     }  
  53. }  
3. I have created View Models to load the static data for testing purpose where departments combobox list few items and new item can be added on it. the mainViewModel is DataContext and each row represents EmployeVM.
 
  1. public class MainViewModel  
  2.     {  
  3.         public ObservableCollection<string> Departments = new ObservableCollection<string> { "Admin""Development""Tranining""Others" };  
  4.   
  5.         public ObservableCollection<EmployeeVM> MainItems { getset; }  
  6.           
  7.         public MainViewModel()  
  8.         {              
  9.             MainItems = new ObservableCollection<EmployeeVM> { new EmployeeVM("Emp1"this), new EmployeeVM("Emp2"this), new EmployeeVM("Emp3"this) };  
  10.         }  
  11.     }  
  12.   
  13.     public class EmployeeVM : INotifyPropertyChanged  
  14.     {  
  15.         private string newDep;  
  16.   
  17.         public EmployeeVM(string name, MainViewModel parent)  
  18.         {  
  19.             Name = name;  
  20.             Parent = parent;  
  21.             department = newDep = "Development";  
  22.         }  
  23.   
  24.         public ObservableCollection<string> Items => Parent.Departments;  
  25.   
  26.         public string Name { getset; }  
  27.         public MainViewModel Parent { get; }  
  28.   
  29.         private string department;  
  30.   
  31.         public string Department  
  32.         {  
  33.             get  
  34.             {  
  35.                 return department;  
  36.             }  
  37.             set  
  38.             {  
  39.                 department = value;  
  40.                 PropertyChanged?.Invoke(thisnew PropertyChangedEventArgs(nameof(Department)));  
  41.             }  
  42.         }  
  43.   
  44.         public string NewDepartment  
  45.         {  
  46.             get  
  47.             {  
  48.                 return newDep;  
  49.             }  
  50.             set  
  51.             {  
  52.                 if (Parent.Departments.FirstOrDefault(s => s.Equals(value)) == null)  
  53.                 {                                     
  54.                     Parent.Departments.Add(value);                     
  55.                 }  
  56.   
  57.                 Department = value;  
  58.                 NewDepartment = value;  
  59.                 PropertyChanged?.Invoke(thisnew PropertyChangedEventArgs(nameof(NewDepartment)));  
  60.             }  
  61.         }  
  62.   
  63.         public event PropertyChangedEventHandler PropertyChanged;  
  64.     }  
Now the behaviour is attached to control. When the user enters it, it listens to keypreview and keydown events and executes the logic.