I maintain a C# .Net 3.5 Visual Studio Windows Form legacy desktop application. In the application, a ListBox’s DataSource utilizes a bounded DataTable filled from a database SQL query.
I want to create a “type-ahead” search for my ListBox. I use a TextBox’s entered text to implement the “type-ahead” search of the ListBox Items and get the index of the first Item that matches the entered text and call method SetSelected to that index. The text to match may be anywhere in the ListBox Items not just at the beginning of the Item.
The ListBox may have thousands of entries therefore, I am trying to use LINQ or some other way to directly find a matching Item instead of iterating through each ListBox item checking if the TextBox.Text occurs anywhere in the ListBox Item. Even when I tried the iteration technique, I did not get the expected results.
private void txtSearch_TextChanged(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txtSearch.Text) == false)
{
for (int i = 0; i <= lbItems.Items.Count - 1; i++)
{
if (lbItems.Items[i].ToString().ToUpper().Contains(txtSearch.Text.ToUpper()))
{
lbItems.SetSelected(i, true);
break;
}
}
}
}
The below works but only matches from the start of the ListBox Item.
private void txtSearch_TextChanged(object sender, EventArgs e)
{
// Ensure we have a string to search.
if (!string.IsNullOrEmpty(txtSearch.Text))
{
// Find the item in the list and store the index to the item.
int index = lbItems.FindString(txtSearch.Text);
// Determine if a valid index is returned. Select the item if it is valid.
if (index != -1)
lbItems.SetSelected(index, true);
else
MessageBox.Show("The search string did not match any items in the ListBox");
}
}
Can I use LINQ or some other way to directly find the index of a ListBox Item that contains a string without iterating through the ListBox Items?
Any other hints as to how to implement a “type-ahead” Item search selection in a ListBox?
The first answer did not work for me as needed. The ListBox just scrolled for too long and no item became selected.
I went the indexing route in an attempt to get a working solution.
The ListBox DataSource binds to a DataTable filled by DataAdapter from a simple SQL database query of a company id and company name from a company table.
OleDbDataAdapter daList = new OleDbDataAdapter();
/*
SQL query (sSQL) of DataAdapter.
SELECT COMP_ID, COMP_ID||' - '||COMP_NAME COMP_NAME FROM COMP ORDER BY COMP_ID
*/
daList.SelectCommand = new OleDbCommand(sSQL, clsMain.conBLASR);
DataTable dtList = new DataTable();
daList.Fill(dtList);
if (dtList.Rows.Count == 0)
{
lbItems.Enabled = false;
lbItems.Items.Add("No data found");
cmdOK.Enabled = false;
}
else
{
//Bind the ListBox to the DataTable.
lbItems.DataSource = dtList;
lbItems.DisplayMember = sDisplayName; // sDisplayName = "COMP_NAME"
bItems.ValueMember = sKeyName; // sKeyName = "COMP_ID"
lbItems.SelectedValue = sKeyValue; // sKeyValue = sCompId
}
The following code works for finding the text in the TextBox within the ListBox and selecting the matching item.
private void txtSearch_TextChanged(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(txtSearch.Text))
{
for (int i = 0; i < lbItems.Items.Count - 1; i++)
{
DataRowView drvLBItem = (DataRowView)lbItems.Items[i];
String lbString = drvLBItem["COMP_NAME"].ToString().ToUpper();
if (lbString.Contains(txtSearch.Text.ToUpper()))
{
lbItems.SetSelected(i, true);
break;
}
}
}
}
I would like to not iterate through all the items and maybe use LINQ or some other technique to directly access the ListBox item that contains the TextBox's text and make it the selected item.