Filter ListBox with TextBox in realtime

2019-02-14 15:20发布

问题:

I am trying to filter an listbox with text from a textbox, realTime.

Here is the code:

private void SrchBox_TextChanged_1(object sender, EventArgs e)
{
  var registrationsList = registrationListBox.Items.Cast<String>().ToList();
  registrationListBox.BeginUpdate();
  registrationListBox.Items.Clear();
  foreach (string str in registrationsList)
  {
    if (str.Contains(SrchBox.Text))
    {
      registrationListBox.Items.Add(str);
    }
  }
  registrationListBox.EndUpdate();
}

Here are the issues:

  1. When I run the program i get this error: Object reference not set to an instance of an object

  2. If I hit backspace, my initial list is not shown anymore. This is because my actual list of items is now reduced, but how can I achieve this?

Can you point me in the right direction?

回答1:

It's hard to deduct just from the code, but I presume your filtering problem born from the different aspects:

a) You need a Model of the data shown on ListBox. You need a colleciton of "Items" which you hold somewhere (Dictionary, DataBase, XML, BinaryFile, Collection), some kind of Store in short.

To show the data on UI you always pick the data from that Store, filter it and put it on UI.

b) After the first point your filtering code can look like this (a pseudocode)

var registrationsList = DataStore.ToList(); //return original data from Store

registrationListBox.BeginUpdate();
registrationListBox.Items.Clear();

if(!string.IsNullOrEmpty(SrchBox.Text)) 
{
  foreach (string str in registrationsList)
  {                
     if (str.Contains(SrchBox.Text))
     {
         registrationListBox.Items.Add(str);
     }
  }
}
else 
   registrationListBox.Items.AddRange(registrationsList); //there is no any filter string, so add all data we have in Store

registrationListBox.EndUpdate();

Hope this helps.



回答2:

Something like this might work for you:

var itemList = registrationListBox.Items.Cast<string>().ToList();
if (itemList.Count > 0)
{
    //clear the items from the list
    registrationListBox.Items.Clear();

    //filter the items and add them to the list
    registrationListBox.Items.AddRange(
        itemList.Where(i => i.Contains(SrchBox.Text)).ToArray());
}


回答3:

Yes that was the answer to filtering. (modified a bit). I had the info in a text file. This is what worked for me

FileInfo registrationsText = new FileInfo(@"name_temp.txt");
            StreamReader registrationsSR = registrationsText.OpenText();
            var registrationsList = registrationListBox.Items.Cast<string>().ToList();

            registrationListBox.BeginUpdate();
            registrationListBox.Items.Clear();

            if (!string.IsNullOrEmpty(SrchBox.Text))
            {
                foreach (string str in registrationsList)
                {
                    if (str.Contains(SrchBox.Text))
                    {
                        registrationListBox.Items.Add(str);
                    }
                }
            }
            else
                while (!registrationsSR.EndOfStream)
                {
                    registrationListBox.Items.Add(registrationsSR.ReadLine());
                }
            registrationListBox.EndUpdate();

It seems that the error:

Object reference not set to an instance of an object

is from somewhere else in my code, can't put my finger on it.



回答4:

If able, store everything in a dictionary and just populate it from there.

public partial class myForm : Form
{
    private Dictionary<string, string> myDictionary = new Dictionary<string, string>();
//constructor. populates the items. Assumes there is a listbox (myListbox) and a textbox (myTextbox), named respectively
public myForm()
{
    InitializeComponent();
    myDictionary.Add("key1", "item1");
    myDictionary.Add("key2", "My Item");
    myDictionary.Add("key3", "A Thing");

    //populate the listbox with everything in the dictionary
    foreach (string s in myDictionary.Values)
        myListbox.Add(s);
}
//make sure to connect this to the textbox change event
private void myTextBox_TextChanged(object sender, EventArgs e)
{
    myListbox.BeginUpdate();
    myListbox.Items.Clear();
    foreach (string s in myDictionary.Values)
    {
        if (s.Contains(myListbox.Text))
            myListbox.Items.Add(s);
    }
    myListbox.EndUpdate();
}
}


回答5:

I would do it like this:

private List<string> registrationsList;

private void SrchBox_TextChanged_1(object sender, EventArgs e)
{
  registrationListBox.BeginUpdate();
  registrationListBox.Items.Clear();

  var filteredList = registrationList.Where(rl => rl.Contains(SrchBox.Text))

  registrationListBox.Items.AddRange();

  registrationListBox.EndUpdate();
}

Just remember to populate registrationsList the first time you fill your listbox.

Hope this helps.



回答6:

it was a very hard issue for me, but I found a workaround (not so simple) that works fine for me.

on aspx page:

<input id="ss" type="text" oninput="writeFilterValue()"/>
<asp:HiddenField ID="hf1" runat="server" Value="" ClientIDMode="Static" />

I need HTML input type because of "oninput" function, that is not availiable on classic asp.net controls. The writeFilterValue() function causes a postback that filters values of a given ListBox (in code-behind).

I've defined this two javascript function:

    <script type="text/javascript">

    function writeFilterValue() {
        var bla = document.getElementById("ss").value;
        $("#hf1").val(bla)
        __doPostBack();
    }

    function setTboxValue(s) {
        document.getElementById('ss').value = s;
        document.getElementById('ss').focus();
    }

</script>

You can now use postback on code-behind to capture hf1 value, every time some single Character is typed on inputbox. On code-behind:

    If IsPostBack Then
        FiltraLbox(hf1.Value)
    End If

The function FiltraLbox(hf1.Value) changes datasource of Listbox, and rebind it:

Public Sub FiltraLbox(ByVal hf As String)

    If hf <> "" Then


    ' change datasource here, that depends on hf value,


        ListBox1.DataBind()

        ScriptManager.RegisterStartupScript(Page, Page.GetType(), "text", setTboxValue('" + hf + "');", True)
    End If

End Sub

At the end I call the function setTboxValue(), that rewrites the input text value lost on postback, and puts the focus on it.

Enjoy it.