XAML / WPF Combobox with search

Date: 2021-01-27
<UserControl x:Class="DNA.UniProductionOrders.Views.Page.EmployeeSelector"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" x:Name="self" VerticalAlignment="Stretch">
    <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch"  Margin="0 0 0 10">
        <Label FontWeight="Bold">Recente werknemers</Label>
        <ItemsControl ItemsSource="{Binding Path=RecentEmployees}" VerticalAlignment="Stretch" Margin="0 0 0 10">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Columns="4" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Click="Button_Click" Content="{Binding Path=KeyName}" Margin="10 5 0 0" Height="30" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Grid VerticalAlignment="Stretch">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50*" />
                <ColumnDefinition Width="50*" />
            </Grid.ColumnDefinitions>

            <Label Grid.Column="0" FontWeight="Bold">Geselecteerde werknemer</Label>
            <ComboBox Grid.Column="1" x:Name="CmbEmployee" 
                      ItemsSource="{Binding Path=Employees}" 
                      SelectedItem="{Binding SelectedEmployee}" 
                      Text="{Binding Path=SearchText,ElementName=self,UpdateSourceTrigger=PropertyChanged}" 
                      IsEditable="True" 
                      StaysOpenOnEdit="True" 
                      DisplayMemberPath="KeyName" 
                      PreviewTextInput="CmbEmployee_PreviewTextInput" SelectionChanged="CmbEmployee_SelectionChanged">
                <!--<ComboBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=KeyName}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>-->
            </ComboBox>
        </Grid>
    </StackPanel>
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Uniconta.ClientTools.DataModel;

namespace DNA.UniProductionOrders.Views.Page
{
    public partial class EmployeeSelector : UserControl
    {
        public IEnumerable<EmployeeClientUser> RecentEmployees { get; set; }
        public IEnumerable<EmployeeClientUser> Employees { get; set; }

        public static readonly DependencyProperty SelectedEmployeeProperty = DependencyProperty.Register(nameof(SelectedEmployee), typeof(EmployeeClientUser), typeof(EmployeeSelector), new PropertyMetadata((EmployeeClient)null));
        public EmployeeClientUser SelectedEmployee
        {
            get => (EmployeeClientUser)GetValue(SelectedEmployeeProperty);
            set {
                SetValue(SelectedEmployeeProperty, value);
                EmployeeSelected?.Invoke(null, value);
            }
        }

        private string _searchText;
        public string SearchText
        {
            get => _searchText;
            set
            {
                if (_searchText == value) return;
                _searchText = value;
                SearchTextChanged();
            }
        }

        public event EventHandler<EmployeeClientUser> EmployeeSelected;

        public EmployeeSelector()
        {
            InitializeComponent();
            DataContext = this;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (!(sender is FrameworkElement source)) return;

            if (source.DataContext is EmployeeClientUser employee)
            {
                CmbEmployee.ItemsSource = Employees;
                SelectedEmployee = employee;
            }
        }

        public void ClearFields()
        {
            SelectedEmployee = null;
        }

        private void CmbEmployee_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedEmployee = CmbEmployee.SelectedItem as EmployeeClientUser;
        }

        private void SearchTextChanged()
        {
            if (string.IsNullOrWhiteSpace(SearchText))
            {
                CmbEmployee.ItemsSource = Employees;
                return;
            }
            var filtered = Employees.Where(em => em.Name.IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) >= 0);
            if (filtered.Any()) { 
                CmbEmployee.ItemsSource = filtered;
            } else {
                CmbEmployee.ItemsSource = Employees;
            }
        }

        private void CmbEmployee_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
        {
            CmbEmployee.IsDropDownOpen = true;
        }
    }
}
45040cookie-checkXAML / WPF Combobox with search