I have a WPF Listbox control and I would like to allow the user to change the selected item by using type-ahead. The behavior I'm looking for is exactly like windows explorer. As you continue to type the text of a folder name, the list will keep selecting the more correct item.
For example assume this folder structure:
OtherFolderName
MyFirstFolder
MyFirstFileFolder
MyFirstList
If you select OtherFolderName
with the mouse, then start typing MyFirstF
the item MyFirstFolder
will be selected, but if you continue typing MyFirstFi
the item MyFirstFileFolder
will be selected.
My WPF Listbox does not exhibit this behavor, I am hoping I can easily enable it, as the old WinForms listbox did just this.
Take a look at the TextSearch class, specifically the TextSearch.TextPath attached property:
<ListBox TextSearch.TextPath="FolderName" ... />
The TextSearch.TextPath property enables text searching and specifies how to extract the search text from each item. In this case I assumed each of your Folder objects has a property named "FolderName".
If this doesn't do everything you're looking for, you'll probably have to implement your own search, since the TextSearch feature isn't particularly tweakable. To do this:
I would build this as a separate class using an attached property, similar to the built-in TextSearch class.
I use a hidden TextBox that appears briefly while the person is typing, and resets after a couple of seconds and clears, so that it does not try matching on its contents after the timer expires. The person would type in the ListBox, and its KeyUp
event will fill in the TextBox because of the binding on SearchText
. When SearchText
is filled, it triggers MyFilteredItems()
to perform a match between that text and the ListBox. Then, if the person presses Enter, the selection would go into another TextBox (not listed in the XAML, but is provided as commented out in the code) and be cleared out of lstPickList
. The TextBox is then cleared and timer resets.
XAML:
<TextBox Name="txtPicker" IsReadOnly="True" Foreground="LightGreen" FontFamily="Consolas" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<ListBox Name="lstPickList" Grid.Row="1" ItemsSource="{Binding MyFilteredItems}" KeyUp="lstPickList_KeyUp"></ListBox>
And then this is the relevant code-behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private Timer t = new Timer();
public System.Windows.Threading.DispatcherTimer tCleanup =
new System.Windows.Threading.DispatcherTimer();
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
_searchText = value;
OnPropertyChanged("SearchText");
OnPropertyChanged("MyFilteredItems");
}
}
public List<string> MyItems { get; set; }
public IEnumerable<string> MyFilteredItems
{
get
{
if (SearchText == null) return MyItems;
return MyItems.Where(x => x.ToUpper().StartsWith(SearchText.ToUpper()));
}
}
public MainWindow()
{
InitializeComponent();
MyItems = new List<string>() { "ABC", "DEF", "GHI" };
this.DataContext = this;
t.Interval = 1000;
t.Elapsed += new ElapsedEventHandler(timerCounter);
tCleanup.Interval = new TimeSpan(0,0,1);
tCleanup.Tick += new EventHandler(cleanupCounter_Tick);
txtPicker.Visibility = Visibility.Collapsed;
tCleanup.Start();
}
private static int counter = 0;
protected void timerCounter(object sender, ElaspedEventArgs e)
{
counter++;
}
protected void cleanupCounter_Tick(object sender, EventArgs e)
{
if (counter > 2 && txtPicker.Visibility == Visibility.Visible)
txtPicker.Visibility = Visibility.Collapsed;
}
private void lstPickList_KeyUp(object sender, KeyEventArgs e)
{
ListBox lst = (ListBox)sender;
string strg = Convert.ToString(e.Key.ToString().Replace("D",""));
if (counter < 2)
{
txtPicker.Visibility = Visibility.Visible;
t.Start();
if (strg == "Return")
{
txtPicker.Text += "{Enter}";
SearchText += "{Enter}";
}
else
{
txtPicker.Text += strg;
SearchText += strg;
}
}
else
{
SearchText = strg;
txtPicker.Text = strg;
t.Stop();
counter = 0;
t.Start();
}
if (strg == "Return")
{
// This next line would be if you had a "selected items" ListBox to store the item
// lstSelectedList.Items.Add(lstPickList.SelectedItem);
lstPickList.Items.Remove(lstPickList.SelectedItem);
t.Stop();
txtPicker.Visibility = Visibility.Collapsed;
counter = 0;
txtPicker.Text = String.Empty;
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With