'WPF - SearchBox based on editable ComboBox
I want to have a search box based on ComboBox that allows users to choose one of the items in a big collection.
Classic ComboBox with IsTextSearchEnabled="true" is almost OK for me, but there are some problems.
- DataBase has about 5000+ entries, so when the listbox of combobox is trying to show me all of the entries, program starts lagging. Thats why i want to show only some of the entries. And i dont want to see anything until users inputs at least 3 chars.
Idea is:
if (searchBox.Text.Length < 3) return;
else show only 10 (or less) entries starts with searchBox.Text.
- Another problem is that i need listbox to be opened automatically when user typed in 3+ symbols. Changing IsDropDownOpen from code causes errors in ComboBox behaviour (like it is just not working or when I type 2nd char 1st disappears).
All in all it should be something like google search bar.
And at the end I can't use any third-party packages or dll's for this program.
Solution 1:[1]
- This feature is called UI virtualization and is already implemented. But while this feature is enabled for the
ListBoxby default, you must explicitly enable UI virtualization for theComboBox(and otherItemsControlsubtypes like theTreeView). This enables the control to load only the items that are visible.
Generally, UI virtualization requires aScrollViewerand aVirtualizationPanel. To enable UI virtualization for theComboBox, you only have to replace the defaultPanelthat hosts the items (which is aStackPanelby default).ComboBoxalready contains aScrollViewerto host its content:
<ComboBox>
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
- The
ComboBoxis a quite complex control. To change its behavior I generally recommend to extend the control to create a customComboBox. Appart fromComboBox.IsEditable, you should also setComboBox.StaysOpenOnEdittotrue.
The following example gives you an example how to implement a solution that opens the drop-down after the third character was entered:
MainWindow.xaml
<ComboBox IsEditable="True"
StaysOpenOnEdit="True"
PreviewTextInput="OnComboBoxPreviewTextInput"
LostFocus="OnComboBoxLostFocus" />
MainWindow.xaml.cs
private StringBuilder SearchInput { get; } = new StringBuilder();
private const int ThresholdInputTextLength = 3;
private void OnComboBoxPreviewTextInput(object sender, TextCompositionEventArgs e)
{
var comboBox = sender as ComboBox;
if (comboBox.IsDropDownOpen)
{
return;
}
this.SearchInput.Append(e.Text);
if (this.SearchInput.Length >= ThresholdInputTextLength)
{
if (TryFindVisualChildElementByName(comboBox, "PART_EditableTextBox", out TextBox editTextBox))
{
var currentSuggestion = editTextBox.Text;
comboBox.IsDropDownOpen = true;
Dispatcher.InvokeAsync(() =>
{
editTextBox.Text = currentSuggestion;
int selectionStartIndex = this.SearchInput.Length;
int selectionLength = currentSuggestion.Length - this.SearchInput.Length;
editTextBox.Select(selectionStartIndex, selectionLength);
this.SearchInput.Clear();
}, DispatcherPriority.Input);
}
}
}
private void OnComboBoxLostFocus(object sender, EventArgs e)
=> this.SearchInput.Clear();
public static bool TryFindVisualChildElementByName<TChild>(
DependencyObject parent,
string childElementName,
out TChild resultElement) where TChild : FrameworkElement
{
resultElement = null;
if (parent is Popup popup)
{
parent = popup.Child;
if (parent == null)
{
return false;
}
}
for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)
{
DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);
if (childElement is TChild frameworkElement)
{
if (string.IsNullOrWhiteSpace(childElementName) || frameworkElement.Name.Equals(
childElementName,
StringComparison.OrdinalIgnoreCase))
{
resultElement = frameworkElement;
return true;
}
}
if (TryFindVisualChildElementByName(childElement, childElementName, out resultElement))
{
return true;
}
}
return false;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
