Phil Phil - 15 days ago 6
C# Question

Searching a muticolumn ListView C#

I have a ListView control which contains 4 columns. Filename, ID, Type, Items. I add items to the ListView by reading in a pipe delimited file which contains the necessary fields. All works great and the ListView displays my data. However I'd like to add a search feature so the user can search through the ListView and filter it's Items to only those which match the search text. I would prefer to be able to have the list filtered as they type, if possible. My listview is using the Details view, with columns defined matching the above 4 columns.

I've tried the following code so far, but just can not figure out how to make it work.
My restoreAllItems method works fine, and restores the listview to contain all items as if the search textbox was empty.

THIS IS A WIN FORMS APPLICATION

Can someone please help point me in the right direction?

Thank you in advance!!!

private void txtJobSearch_TextChanged(object sender, EventArgs e)
{
// Search items in our Jobs ListView, remove those that do not match search
if (txtJobSearch.Text != String.Empty)
{
for (int i = listJobs.Items.Count - 1; i >= 0; i--)
{
if (listJobs.Items[i].Text.ToLower().Contains(txtJobSearch.Text.ToLower()))
{
listJobs.Items[i].BackColor = SystemColors.Highlight;
listJobs.Items[i].ForeColor = SystemColors.HighlightText;
}
else
{
for (int x = listJobs.Items[i].SubItems.Count - 1; x >= listJobs.Items[i].SubItems.Count; x--)
{
if (listJobs.Items[i].SubItems[x].Text.ToLower().Contains(txtJobSearch.Text.ToLower()))
{
listJobs.Items[i].BackColor = SystemColors.Highlight;
listJobs.Items[i].ForeColor = SystemColors.HighlightText;
}
else
{
listJobs.Items[i].Remove();
}
}
}
}
}
else
{
restoreAllItems("jobs");
}
}

Answer

I've updated your code a bit. Highlighting logic is removed since I don't fully understand it's purpose, but you can easily put it back in place. The following code operates on the same form as yours. The only exception is ReinitList() method that is implemented for testing purposes.

// check if current item matches search string
private bool ItemMatches(ListViewItem item, string text)
{
    bool matches = false;

    matches |= item.Text.ToLower().Contains(text.ToLower());

    if (matches)
    {
        return true;
    }

    foreach (ListViewItem.ListViewSubItem subitem in item.SubItems)
    {
        matches |= subitem.Text.ToLower().Contains(text.ToLower());
        if (matches)
        {
            return true;
        }
    }

    return false;
}

private void txtJobSearch_TextChanged(object sender, EventArgs e)
{
    // prevent flickering
    listJobs.BeginUpdate();

    // restore all items in case user deletes some characters in the textbox
    ReinitList();

    string search = txtJobSearch.Text;
    // Search items in our Jobs ListView, remove those that do not match search
    if (search != String.Empty)
    {
        for (int i = listJobs.Items.Count - 1; i >= 0; i--)
        {
            ListViewItem currentItem = listJobs.Items[i];
            if (ItemMatches(currentItem, search))
            {
                // highlight, or move highlighting to ItemMatches
            }
            else
            {
                listJobs.Items.RemoveAt(i);
            }
        }
    }

    listJobs.EndUpdate();
}


private void ReinitList()
{
    listJobs.Items.Clear();
    for (int i = 0; i < 50; i++)
    {
        ListViewItem item = new ListViewItem(i.ToString());
        item.SubItems.Add("sub " + i.ToString());
        item.SubItems.Add("sub a" + i.ToString());
        item.SubItems.Add("sub b" + i.ToString());

        listJobs.Items.Add(item);
    }
}

private void Form1_Load(object sender, EventArgs e)
{
    ReinitList();
}