Reputation: 119
I'm trying to implement an auto-search functionality for a WPF ComboBox, where the combo box filters its items as the user types, and allows them to select from the filtered list. However, after selecting an item from the filtered list, the selected item does not display in the ComboBox.
Expected behavior: When the user starts typing, the ComboBox filters and displays matching items. After selecting an item from the filtered list, the ComboBox should display the selected item in the editable textbox. Problem: The ComboBox behaves as expected when typing, filtering items based on the entered text. But once the user selects a filtered item, it doesn't show up in the ComboBox.
What I've tried: I have implemented custom logic to update the SelectedItem and SearchText properties when the user selects an item. I’ve also added handling for SelectionChanged and PreviewKeyDown events to manage the interaction between the ComboBox and the search functionality. However, the selected value does not display in the ComboBox as expected.
Here’s a simplified version of my code:
xaml:
<ComboBox
x:Name="ComboBoxControl"
Width="500"
Margin="10"
materialDesign:HintAssist.Foreground="DeepPink"
materialDesign:HintAssist.Hint="Select Type"
materialDesign:TextFieldAssist.HasClearButton="True"
IsEditable="True"
IsTextSearchCaseSensitive="False"
IsTextSearchEnabled="False"
ItemsSource="{Binding FilteredItems}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionChanged="ComboBoxControl_SelectionChanged"
Style="{StaticResource MaterialDesignOutlinedComboBox}"
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
backened code:
namespace WpfApp1.CustomControls
{
public partial class AutoSearchComboBox : UserControl, INotifyPropertyChanged
{
public ObservableCollection<string> ItemsSource { get; set; }
public ObservableCollection<string> FilteredItems { get; set; } = new ObservableCollection<string>();
private string _selectedItem;
public string SelectedItem
{
get => _selectedItem;
set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged(nameof(SelectedItem));
if (!_isUpdatingFromCode)
{
SearchText = _selectedItem; // Sync selected item to search text
}
}
}
}
private string _searchText;
public string SearchText
{
get => _searchText;
set
{
if (_searchText != value)
{
_searchText = value;
if (!_isUpdatingFromCode)
{
FilterItems(); // Trigger filtering when SearchText changes
}
OnPropertyChanged(nameof(SearchText));
}
}
}
private bool _isUserTyping = false;
private bool _isUpdatingFromCode = false;
public AutoSearchComboBox()
{
InitializeComponent();
DataContext = this;
Loaded += AutoSearchComboBox_Loaded;
ComboBoxControl.PreviewKeyDown += ComboBoxControl_PreviewKeyDown;
ComboBoxControl.SelectionChanged += ComboBoxControl_SelectionChanged;
}
private void ComboBoxControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!_isUserTyping && ComboBoxControl.SelectedItem != null && !_isUpdatingFromCode)
{
ComboBoxControl.IsEditable = false;
Dispatcher.BeginInvoke(new Action(() =>
{
SearchText = ComboBoxControl.SelectedItem.ToString(); // Update SearchText
SelectedItem = ComboBoxControl.SelectedItem.ToString(); // Sync SelectedItem
_isUpdatingFromCode = false;
}), System.Windows.Threading.DispatcherPriority.Background);
ComboBoxControl.IsEditable = true;
}
}
private void ComboBoxControl_PreviewKeyDown(object sender, KeyEventArgs e)
{
_isUserTyping = true;
var textBox = ComboBoxControl.Template.FindName("PART_EditableTextBox", ComboBoxControl) as TextBox;
if (textBox != null)
{
if (textBox.SelectionLength > 0) textBox.SelectionLength = 0;
textBox.CaretIndex = textBox.Text.Length;
}
_isUserTyping = false;
}
private void AutoSearchComboBox_Loaded(object sender, RoutedEventArgs e)
{
if (ItemsSource != null)
{
FilteredItems.Clear();
foreach (var item in ItemsSource)
{
FilteredItems.Add(item);
}
}
}
private void FilterItems()
{
if (_isUpdatingFromCode) return;
FilteredItems.Clear();
if (string.IsNullOrWhiteSpace(SearchText))
{
foreach (var item in ItemsSource)
{
FilteredItems.Add(item);
}
}
else
{
foreach (var item in ItemsSource.Where(i => i.StartsWith(SearchText, StringComparison.OrdinalIgnoreCase) || i.Contains(SearchText, StringComparison.OrdinalIgnoreCase)))
{
FilteredItems.Add(item);
}
}
ComboBoxControl.IsDropDownOpen = true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Upvotes: 0
Views: 26