2

I have a listview name listView1 and it contains computer id's and their some infos.So what I want to do is I have a textbox name filterbox and when I write something it will filter it,it works fine.My problem is it just look for the first column not the others.For example ;

PCNAME  USER  MODEL
 AAAA   JOHN  DELL
 BBBB   MIKE  TOSHIBA
 CCCC   ASH   MONSTER

when I type BB it just gets the second row and it works fine but if I type DELL it gets me nothing.

  private void filterbox_TextChanged(object sender, EventArgs e)
        {
            if (filterbox.Text != "")
            {
                for (int i = listView1.Items.Count - 1; i >= 0; i--)
                {
                    var item = listView1.Items[i];
                    if (item.Text.ToLower().Contains(filterbox.Text.ToLower()))
                    {
                        item.BackColor = SystemColors.Highlight;
                        item.ForeColor = SystemColors.HighlightText;
                    }
                    else
                    {
                        listView1.Items.Remove(item);
                    }
                }
                if (listView1.SelectedItems.Count == 1)
                {
                    listView1.Focus();
                }
            }
        }
Darth Sucuk
  • 194
  • 2
  • 16

3 Answers3

3

It's as simple as iterating the sub items:

foreach (ListViewItem item in listView1.Items)
   foreach (ListViewItem.ListViewSubItem subitem in item.SubItems)
      if (subitem.Text.Equals(filterbox.Text, StringComparison.OrdinalIgnoreCase))
      {
         item.BackColor = SystemColors.Highlight;
         item.ForeColor = SystemColors.HighlightText;
         break;
      }

Note, you had other things going on here, however I'll leave them details up to you.

Update

For filtering you could use some Linq:

if (filterbox.Text == "")
{
   return;
}

var list = listView1.Items
                    .Cast<ListViewItem>()
                    .Where(
                        x => x.SubItems
                              .Cast<ListViewItem.ListViewSubItem>()
                              .Any(y => y.Text.Contains(filterbox.Text)))
                    .ToArray();
listView1.Items.Clear();
listView1.Items.AddRange(list);
Community
  • 1
  • 1
TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • How about filtering? – Access Denied Sep 20 '18 at 08:24
  • You should add the items to a List and remove them from the LV to create a filter. Whenever the TextBox changes you need to re-add them before filtering again. – TaW Sep 20 '18 at 08:25
  • that's not what I want it just highlight it not remove anyone – Darth Sucuk Sep 20 '18 at 08:33
  • @DarthSucuk, Instead of `var list = listView1.Items` a `var list = listViewDataSource.Items` will allow you to not definitivly clear item for the list. If you filter the list then update the list the list wont containt the item you delete anymore .. the source must be outside. – Drag and Drop Sep 20 '18 at 08:45
  • 1
    As in the OP a text.Contains(searchText) is called for instead of Equals. – TaW Sep 20 '18 at 08:47
  • And `OfType` instead of `Cast` as a minor optimisation. – Drag and Drop Sep 20 '18 at 08:51
  • @DarthSucuk you have to many problems to really address here, you need to keep your list in memory in a list of class or a db, and filter and update dynamically. because you are doing this on key change, you are going to run out of items in the list. – TheGeneral Sep 20 '18 at 08:59
  • 2
    _it gives tons of error_ - Nonsense. It gives __one__ error; remove `, StringComparison.OrdinalIgnoreCase` chnaging the line to `.Any(y => y.Text.Contains(filterbox.Text)))`and the code works just fine. – TaW Sep 20 '18 at 08:59
  • 1
    @TaW you are right it's work it's filtering with every column but there one more problem when I delete characters it's give me nothing? – Darth Sucuk Sep 20 '18 at 09:47
  • 1
    From @TaW code it filter it but changing text again it will give a blank page – Nuisance Sep 20 '18 at 09:53
  • 1
    Indeed, the answer is not complete. Here are steps to correct: create a class level variable to hold all items: `List allItems = new List();` . Fill it when the items are added: `allItems.AddRange(listView1.Items.Cast());`. And use it during filtering: `var list = allItems .Cast()..` – TaW Sep 20 '18 at 09:54
  • @TaW I'm a little bit new in these case so could you please post your answer,so I can accept your post as an answer you really helped me you earned this :) – Darth Sucuk Sep 20 '18 at 10:11
  • @TaW answer it up and I'll up vote, I'm not in front of the pc – TheGeneral Sep 20 '18 at 11:16
3

The core of Saruman's answer works pretty well: Copy all items that fit the criteria into an enumerable collection with linq, clear the listview items and AddRange the good ones.

A few corrections are needed however for a fully working solution.

First: We need store the full set of items somewhere. Best in a class level variable:

List<ListViewItem> allItems = new List<ListViewItem>();

We need to fill it after the full set of items has been added:

allItems.Clear();
allItems.AddRange(listView1.Items.Cast<ListViewItem>());

Now we can code the TextChanged event of our filterTextbox; here we always use the full set of data:

private void filterbox_TextChanged(object sender, EventArgs e)
{
    listView1.Items.Clear();   // clear all items we have atm
    if (filterbox.Text == "")
    {
        listView1.Items.AddRange(allItems.ToArray());  // no filter: add all items
        return;
    }
    // now we find all items that have a suitable text in any subitem/field/column
    var list = allItems.Cast<ListViewItem>()
                       .Where( x => x.SubItems
                                     .Cast<ListViewItem.ListViewSubItem>()
                                     .Any(y => y.Text.Contains(filterbox.Text)))
                       .ToArray();
    listView1.Items.AddRange(list);  // now we add the result
}

Kudos to Saruman who wrote the linq with the necessary Casts!

Btw: We should and do use Cast instead of OfType because we can be sure of the types. Cast is a bit faster.

TaW
  • 53,122
  • 8
  • 69
  • 111
1

You can bind your list-view with data table. refer bind list-view with data-table . And on text-box "text changed/control leave" event apply filter on data-table. This will take care of updating items of the list-view as it's data source is bound to data-table.

listView1.ItemsSource = dataTable.DefaultView;

Filter data using below query.

DataView dv = new DataView(yourDatatable);
dv.RowFilter = "query"; // query example = "id = 10"

Let me know if it helps. :)

Ketan
  • 79
  • 1
  • 9
  • The RowFileter would be nice but ListView (containing jagged arrays) is notoriously bad at databinding to tables. – TaW Sep 20 '18 at 11:14